// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/tag/assigntag.go
package tag

import (
	"context"
	"fmt"
	"time"

	"github.com/gocql/gocql"

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

type AssignTagUseCase struct {
	tagRepo        dom_tag.Repository
	collectionRepo dom_collection.CollectionRepository
	fileRepo       dom_file.FileMetadataRepository
}

func NewAssignTagUseCase(
	tagRepo dom_tag.Repository,
	collectionRepo dom_collection.CollectionRepository,
	fileRepo dom_file.FileMetadataRepository,
) *AssignTagUseCase {
	return &AssignTagUseCase{
		tagRepo:        tagRepo,
		collectionRepo: collectionRepo,
		fileRepo:       fileRepo,
	}
}

func (uc *AssignTagUseCase) Execute(ctx context.Context, userID, tagID, entityID gocql.UUID, entityType string) error {
	// Validate entity type
	if entityType != dom_tag.EntityTypeCollection && entityType != dom_tag.EntityTypeFile {
		return fmt.Errorf("invalid entity type: %s", entityType)
	}

	// Verify tag exists and belongs to user
	tag, err := uc.tagRepo.GetByID(ctx, tagID)
	if err != nil {
		return fmt.Errorf("tag not found: %w", err)
	}
	if tag.UserID != userID {
		return fmt.Errorf("unauthorized: tag does not belong to user")
	}

	// Handle collection or file assignment
	switch entityType {
	case dom_tag.EntityTypeCollection:
		return uc.assignToCollection(ctx, userID, tag, entityID)
	case dom_tag.EntityTypeFile:
		return uc.assignToFile(ctx, userID, tag, entityID)
	default:
		return fmt.Errorf("unsupported entity type: %s", entityType)
	}
}

func (uc *AssignTagUseCase) assignToCollection(ctx context.Context, userID gocql.UUID, tag *dom_tag.Tag, collectionID gocql.UUID) error {
	// Get collection
	collection, err := uc.collectionRepo.Get(ctx, collectionID)
	if err != nil {
		return fmt.Errorf("collection not found: %w", err)
	}

	// Check if tag is already assigned
	for _, existingTag := range collection.Tags {
		if existingTag.ID == tag.ID {
			return nil // Already assigned, idempotent
		}
	}

	// Add embedded tag to collection's tag list
	embeddedTag := tag.ToEmbeddedTag()
	collection.Tags = append(collection.Tags, *embeddedTag)
	collection.ModifiedAt = time.Now()

	// Update collection (this will trigger denormalized table maintenance in the repository)
	if err := uc.collectionRepo.Update(ctx, collection); err != nil {
		return fmt.Errorf("failed to update collection: %w", err)
	}

	// Create lightweight assignment tracking
	assignment := &dom_tag.TagAssignment{
		ID:         gocql.TimeUUID(),
		UserID:     userID,
		TagID:      tag.ID,
		EntityID:   collectionID,
		EntityType: dom_tag.EntityTypeCollection,
		CreatedAt:  time.Now(),
	}

	if err := uc.tagRepo.AssignTag(ctx, assignment); err != nil {
		return fmt.Errorf("failed to create tag assignment: %w", err)
	}

	return nil
}

func (uc *AssignTagUseCase) assignToFile(ctx context.Context, userID gocql.UUID, tag *dom_tag.Tag, fileID gocql.UUID) error {
	// Get file metadata
	file, err := uc.fileRepo.Get(fileID)
	if err != nil {
		return fmt.Errorf("file not found: %w", err)
	}

	// Check if tag is already assigned
	for _, existingTag := range file.Tags {
		if existingTag.ID == tag.ID {
			return nil // Already assigned, idempotent
		}
	}

	// Add embedded tag to file's tag list
	embeddedTag := tag.ToEmbeddedTag()
	file.Tags = append(file.Tags, *embeddedTag)
	file.ModifiedAt = time.Now()

	// Update file (this will trigger denormalized table maintenance in the repository)
	if err := uc.fileRepo.Update(file); err != nil {
		return fmt.Errorf("failed to update file: %w", err)
	}

	// Create lightweight assignment tracking
	assignment := &dom_tag.TagAssignment{
		ID:         gocql.TimeUUID(),
		UserID:     userID,
		TagID:      tag.ID,
		EntityID:   fileID,
		EntityType: dom_tag.EntityTypeFile,
		CreatedAt:  time.Now(),
	}

	if err := uc.tagRepo.AssignTag(ctx, assignment); err != nil {
		return fmt.Errorf("failed to create tag assignment: %w", err)
	}

	return nil
}
