package dash

import (
	"context"
	"path"
	"strings"
	"time"

	"github.com/seaweedfs/seaweedfs/weed/glog"
	"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
	"github.com/seaweedfs/seaweedfs/weed/s3api/s3tables"
)

// FileEntry represents a file or directory entry in the file browser
type FileEntry struct {
	Name        string    `json:"name"`
	FullPath    string    `json:"full_path"`
	IsDirectory bool      `json:"is_directory"`
	Size        int64     `json:"size"`
	ModTime     time.Time `json:"mod_time"`
	Mode        string    `json:"mode"`
	Uid         uint32    `json:"uid"`
	Gid         uint32    `json:"gid"`
	Mime        string    `json:"mime"`
	Replication string    `json:"replication"`
	Collection  string    `json:"collection"`
	TtlSec      int32     `json:"ttl_sec"`
}

// BreadcrumbItem represents a single breadcrumb in the navigation
type BreadcrumbItem struct {
	Name string `json:"name"`
	Path string `json:"path"`
}

// FileBrowserData contains all data needed for the file browser view
type FileBrowserData struct {
	Username    string           `json:"username"`
	CurrentPath string           `json:"current_path"`
	ParentPath  string           `json:"parent_path"`
	Breadcrumbs []BreadcrumbItem `json:"breadcrumbs"`
	Entries     []FileEntry      `json:"entries"`

	LastUpdated       time.Time `json:"last_updated"`
	IsBucketPath      bool      `json:"is_bucket_path"`
	BucketName        string    `json:"bucket_name"`
	IsTableBucketPath bool      `json:"is_table_bucket_path"`
	TableBucketName   string    `json:"table_bucket_name"`
	// Pagination fields
	PageSize            int    `json:"page_size"`
	HasNextPage         bool   `json:"has_next_page"`
	LastFileName        string `json:"last_file_name"`         // Cursor for next page
	CurrentLastFileName string `json:"current_last_file_name"` // Cursor from current request (for page size changes)
}

// GetFileBrowser retrieves file browser data for a given path with cursor-based pagination
func (s *AdminServer) GetFileBrowser(dir string, lastFileName string, pageSize int) (*FileBrowserData, error) {
	if dir == "" {
		dir = "/"
	}

	// Set defaults for pagination
	if pageSize < 1 {
		pageSize = 20 // Default page size
	}

	var entries []FileEntry

	// Fetch entries using cursor-based pagination
	// We fetch pageSize+1 to determine if there's a next page
	fetchLimit := pageSize + 1
	var fetchedCount int
	var lastEntryName string

	err := s.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
		// Fetch entries starting from the cursor (lastFileName)
		stream, err := client.ListEntries(context.Background(), &filer_pb.ListEntriesRequest{
			Directory:          dir,
			Prefix:             "",
			Limit:              uint32(fetchLimit),
			StartFromFileName:  lastFileName,
			InclusiveStartFrom: false, // Don't include the cursor file itself
		})
		if err != nil {
			return err
		}

		for {
			resp, err := stream.Recv()
			if err != nil {
				if err.Error() == "EOF" {
					break
				}
				return err
			}

			entry := resp.Entry
			if entry == nil {
				continue
			}

			fetchedCount++

			// Only add entries up to pageSize (the +1 is just to check for next page)
			if fetchedCount <= pageSize {
				fullPath := path.Join(dir, entry.Name)

				var modTime time.Time
				if entry.Attributes != nil && entry.Attributes.Mtime > 0 {
					modTime = time.Unix(entry.Attributes.Mtime, 0)
				}

				var mode string
				var uid, gid uint32
				var size int64
				var replication, collection string
				var ttlSec int32

				if entry.Attributes != nil {
					mode = FormatFileMode(entry.Attributes.FileMode)
					uid = entry.Attributes.Uid
					gid = entry.Attributes.Gid
					size = int64(entry.Attributes.FileSize)
					ttlSec = entry.Attributes.TtlSec
				}

				// Get replication and collection from entry extended attributes
				if entry.Extended != nil {
					if repl, ok := entry.Extended["replication"]; ok {
						replication = string(repl)
					}
					if coll, ok := entry.Extended["collection"]; ok {
						collection = string(coll)
					}
				}

				mime := ResolveEntryMime(entry)

				fileEntry := FileEntry{
					Name:        entry.Name,
					FullPath:    fullPath,
					IsDirectory: entry.IsDirectory,
					Size:        size,
					ModTime:     modTime,
					Mode:        mode,
					Uid:         uid,
					Gid:         gid,
					Mime:        mime,
					Replication: replication,
					Collection:  collection,
					TtlSec:      ttlSec,
				}

				entries = append(entries, fileEntry)

				lastEntryName = entry.Name

			}
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	// Determine if there's a next page
	hasNextPage := fetchedCount > pageSize

	// Generate breadcrumbs
	breadcrumbs := s.generateBreadcrumbs(dir)

	// Calculate parent path
	parentPath := "/"
	if dir != "/" {
		parentPath = path.Dir(dir)
		if parentPath == "." {
			parentPath = "/"
		}
	}

	// Check if this is a bucket path and determine if it's a table bucket
	isBucketPath := false
	bucketName := ""
	isTableBucketPath := false
	tableBucketName := ""
	if strings.HasPrefix(dir, "/buckets/") {
		isBucketPath = true
		pathParts := strings.Split(strings.Trim(dir, "/"), "/")
		if len(pathParts) >= 2 {
			bucketName = pathParts[1]
			// Check table bucket status early to avoid second WithFilerClient call
			if err := s.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
				resp, err := filer_pb.LookupEntry(context.Background(), client, &filer_pb.LookupDirectoryEntryRequest{
					Directory: "/buckets",
					Name:      bucketName,
				})
				if err != nil {
					return err
				}
				if s3tables.IsTableBucketEntry(resp.Entry) {
					isTableBucketPath = true
					tableBucketName = bucketName
				}
				return nil
			}); err != nil {
				glog.V(1).Infof("file browser table bucket lookup failed for %s: %v", bucketName, err)
			}
		}
	}

	return &FileBrowserData{
		CurrentPath: dir,
		ParentPath:  parentPath,
		Breadcrumbs: breadcrumbs,
		Entries:     entries,

		LastUpdated:       time.Now(),
		IsBucketPath:      isBucketPath,
		BucketName:        bucketName,
		IsTableBucketPath: isTableBucketPath,
		TableBucketName:   tableBucketName,
		// Pagination metadata
		PageSize:            pageSize,
		HasNextPage:         hasNextPage,
		LastFileName:        lastEntryName, // Store for next page navigation
		CurrentLastFileName: lastFileName,  // Store input cursor for page size changes
	}, nil
}

// generateBreadcrumbs creates breadcrumb navigation for the current path
func (s *AdminServer) generateBreadcrumbs(dir string) []BreadcrumbItem {
	var breadcrumbs []BreadcrumbItem

	// Always start with root
	breadcrumbs = append(breadcrumbs, BreadcrumbItem{
		Name: "Root",
		Path: "/",
	})

	if dir == "/" {
		return breadcrumbs
	}

	// Split path and build breadcrumbs
	parts := strings.Split(strings.Trim(dir, "/"), "/")
	currentPath := ""

	for _, part := range parts {
		if part == "" {
			continue
		}
		currentPath += "/" + part

		// Special handling for bucket paths
		displayName := part
		if len(breadcrumbs) == 1 && part == "buckets" {
			displayName = "Object Store Buckets"
		} else if len(breadcrumbs) == 2 && strings.HasPrefix(dir, "/buckets/") {
			displayName = "📦 " + part // Add bucket icon to bucket name
		}

		breadcrumbs = append(breadcrumbs, BreadcrumbItem{
			Name: displayName,
			Path: currentPath,
		})
	}

	return breadcrumbs
}
