// monorepo/cloud/backend/internal/maplefile/service/collection/get_filtered.go
package collection

import (
	"context"

	"go.uber.org/zap"

	"github.com/gocql/gocql"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config/constants"
	dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
)

type GetFilteredCollectionsRequestDTO struct {
	IncludeOwned  bool `json:"include_owned"`
	IncludeShared bool `json:"include_shared"`
}

type FilteredCollectionsResponseDTO struct {
	OwnedCollections  []*CollectionResponseDTO `json:"owned_collections"`
	SharedCollections []*CollectionResponseDTO `json:"shared_collections"`
	TotalCount        int                      `json:"total_count"`
}

type GetFilteredCollectionsService interface {
	Execute(ctx context.Context, req *GetFilteredCollectionsRequestDTO) (*FilteredCollectionsResponseDTO, error)
}

type getFilteredCollectionsServiceImpl struct {
	config   *config.Configuration
	logger   *zap.Logger
	repo     dom_collection.CollectionRepository
}

func NewGetFilteredCollectionsService(
	config *config.Configuration,
	logger *zap.Logger,
	repo dom_collection.CollectionRepository,
) GetFilteredCollectionsService {
	logger = logger.Named("GetFilteredCollectionsService")
	return &getFilteredCollectionsServiceImpl{
		config:   config,
		logger:   logger,
		repo:     repo,
	}
}

func (svc *getFilteredCollectionsServiceImpl) Execute(ctx context.Context, req *GetFilteredCollectionsRequestDTO) (*FilteredCollectionsResponseDTO, error) {
	//
	// STEP 1: Validation
	//
	if req == nil {
		svc.logger.Warn("Failed validation with nil request")
		return nil, httperror.NewForBadRequestWithSingleField("non_field_error", "Request details are required")
	}

	e := make(map[string]string)
	if !req.IncludeOwned && !req.IncludeShared {
		e["filter_options"] = "At least one filter option (include_owned or include_shared) must be enabled"
	}
	if len(e) != 0 {
		svc.logger.Warn("Failed validation",
			zap.Any("error", e))
		return nil, httperror.NewForBadRequest(&e)
	}

	//
	// STEP 2: Get user ID from context
	//
	userID, ok := ctx.Value(constants.SessionUserID).(gocql.UUID)
	if !ok {
		svc.logger.Error("Failed getting user ID from context")
		return nil, httperror.NewForInternalServerErrorWithSingleField("message", "Authentication context error")
	}

	//
	// STEP 3: Create filter options
	//
	filterOptions := dom_collection.CollectionFilterOptions{
		IncludeOwned:  req.IncludeOwned,
		IncludeShared: req.IncludeShared,
		UserID:        userID,
	}

	//
	// STEP 4: Get filtered collections from repository
	//
	result, err := svc.repo.GetCollectionsWithFilter(ctx, filterOptions)
	if err != nil {
		svc.logger.Error("Failed to get filtered collections",
			zap.Any("error", err),
			zap.Any("user_id", userID),
			zap.Any("filter_options", filterOptions))
		return nil, err
	}

	//
	// STEP 5: Filter collections based on permission levels and map to DTOs
	//
	// For owned collections, the owner always has admin permission
	ownedCollectionsWithPermission := make([]*CollectionResponseDTO, 0, len(result.OwnedCollections))
	for _, collection := range result.OwnedCollections {
		// Owner always has full access, no need to check permission
		ownerEmail := getOwnerEmailFromMembers(collection)
		ownedCollectionsWithPermission = append(ownedCollectionsWithPermission,
			mapCollectionToDTO(collection, int(collection.FileCount), ownerEmail))
	}

	// For shared collections, verify the user has at least read_only permission
	sharedCollectionsWithPermission := make([]*CollectionResponseDTO, 0, len(result.SharedCollections))
	for _, collection := range result.SharedCollections {
		// Check if user has at least read_only permission for this shared collection
		hasAccess, err := svc.repo.CheckAccess(ctx, collection.ID, userID, dom_collection.CollectionPermissionReadOnly)
		if err != nil {
			svc.logger.Warn("Failed to check collection access, skipping collection",
				zap.Error(err),
				zap.Any("collection_id", collection.ID),
				zap.Any("user_id", userID))
			continue // Skip collections where we can't verify access
		}

		if hasAccess {
			ownerEmail := getOwnerEmailFromMembers(collection)
			sharedCollectionsWithPermission = append(sharedCollectionsWithPermission,
				mapCollectionToDTO(collection, int(collection.FileCount), ownerEmail))
		} else {
			svc.logger.Debug("User lacks permission for shared collection, filtering out",
				zap.Any("collection_id", collection.ID),
				zap.Any("user_id", userID))
		}
	}

	response := &FilteredCollectionsResponseDTO{
		OwnedCollections:  ownedCollectionsWithPermission,
		SharedCollections: sharedCollectionsWithPermission,
		TotalCount:        len(ownedCollectionsWithPermission) + len(sharedCollectionsWithPermission),
	}

	svc.logger.Debug("Retrieved filtered collections successfully",
		zap.Int("owned_count", len(response.OwnedCollections)),
		zap.Int("shared_count", len(response.SharedCollections)),
		zap.Int("total_count", response.TotalCount),
		zap.Any("user_id", userID))

	return response, nil
}
