// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/service/tag/search_by_tags.go
package tag

import (
	"context"
	"sync"

	"github.com/gocql/gocql"
	"go.uber.org/zap"

	dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
	dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
	uc_tag "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/tag"
)

// SearchByTagsService orchestrates searching for both collections and files by tags
type SearchByTagsService struct {
	logger            *zap.Logger
	listCollectionsUC *uc_tag.ListCollectionsByTagUseCase
	listFilesUC       *uc_tag.ListFilesByTagUseCase
}

// NewSearchByTagsService creates a new search by tags service
func NewSearchByTagsService(
	logger *zap.Logger,
	listCollectionsUC *uc_tag.ListCollectionsByTagUseCase,
	listFilesUC *uc_tag.ListFilesByTagUseCase,
) *SearchByTagsService {
	return &SearchByTagsService{
		logger:            logger.Named("SearchByTagsService"),
		listCollectionsUC: listCollectionsUC,
		listFilesUC:       listFilesUC,
	}
}

// SearchByTagsRequest represents the input for searching by tags
type SearchByTagsRequest struct {
	UserID gocql.UUID
	TagIDs []gocql.UUID
	Limit  int // Total results limit (split between collections and files)
}

// SearchByTagsResponse represents the unified search results
type SearchByTagsResponse struct {
	Collections     []*dom_collection.Collection `json:"collections"`
	Files           []*dom_file.File             `json:"files"`
	TagCount        int                          `json:"tag_count"`
	CollectionCount int                          `json:"collection_count"`
	FileCount       int                          `json:"file_count"`
}

// Execute performs the unified tag search, querying both collections and files in parallel
func (s *SearchByTagsService) Execute(ctx context.Context, req *SearchByTagsRequest) (*SearchByTagsResponse, error) {
	// Validate input
	if req == nil {
		return nil, nil
	}

	if len(req.TagIDs) == 0 {
		return &SearchByTagsResponse{
			Collections:     []*dom_collection.Collection{},
			Files:           []*dom_file.File{},
			TagCount:        0,
			CollectionCount: 0,
			FileCount:       0,
		}, nil
	}

	// Set default limit if not specified
	limit := req.Limit
	if limit <= 0 {
		limit = 50
	}
	if limit > 100 {
		limit = 100
	}

	// Split the limit between collections and files
	// Give each half of the total limit
	collectionsLimit := limit / 2
	filesLimit := limit / 2

	// Use WaitGroup to execute both queries in parallel
	var wg sync.WaitGroup
	wg.Add(2)

	var collections []*dom_collection.Collection
	var files []*dom_file.File
	var collectionsErr error
	var filesErr error

	// Query collections
	go func() {
		defer wg.Done()
		collections, _, collectionsErr = s.listCollectionsUC.Execute(ctx, req.UserID, req.TagIDs, collectionsLimit, "")
		if collectionsErr != nil {
			s.logger.Warn("Failed to list collections by tags",
				zap.Error(collectionsErr),
				zap.Int("tag_count", len(req.TagIDs)))
		}
	}()

	// Query files
	go func() {
		defer wg.Done()
		files, _, filesErr = s.listFilesUC.Execute(ctx, req.UserID, req.TagIDs, filesLimit, "")
		if filesErr != nil {
			s.logger.Warn("Failed to list files by tags",
				zap.Error(filesErr),
				zap.Int("tag_count", len(req.TagIDs)))
		}
	}()

	// Wait for both queries to complete
	wg.Wait()

	// Handle errors - if both failed, return error
	// If only one failed, continue with partial results
	if collectionsErr != nil && filesErr != nil {
		s.logger.Error("Both collection and file queries failed",
			zap.Error(collectionsErr),
			zap.NamedError("files_error", filesErr))
		return nil, collectionsErr // Return first error
	}

	// Ensure we have non-nil slices
	if collections == nil {
		collections = []*dom_collection.Collection{}
	}
	if files == nil {
		files = []*dom_file.File{}
	}

	response := &SearchByTagsResponse{
		Collections:     collections,
		Files:           files,
		TagCount:        len(req.TagIDs),
		CollectionCount: len(collections),
		FileCount:       len(files),
	}

	s.logger.Info("Tag search completed",
		zap.Int("tag_count", len(req.TagIDs)),
		zap.Int("collections_found", len(collections)),
		zap.Int("files_found", len(files)))

	return response, nil
}
