// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/tag/unassigntag.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 UnassignTagUseCase struct {
	tagRepo        dom_tag.Repository
	collectionRepo dom_collection.CollectionRepository
	fileRepo       dom_file.FileMetadataRepository
}

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

func (uc *UnassignTagUseCase) Execute(ctx context.Context, 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)
	}

	// Handle collection or file unassignment
	switch entityType {
	case dom_tag.EntityTypeCollection:
		return uc.unassignFromCollection(ctx, tagID, entityID)
	case dom_tag.EntityTypeFile:
		return uc.unassignFromFile(ctx, tagID, entityID)
	default:
		return fmt.Errorf("unsupported entity type: %s", entityType)
	}
}

func (uc *UnassignTagUseCase) unassignFromCollection(ctx context.Context, tagID, collectionID gocql.UUID) error {
	// Get collection
	collection, err := uc.collectionRepo.Get(ctx, collectionID)
	if err != nil {
		return fmt.Errorf("collection not found: %w", err)
	}

	// Remove tag from collection's tag list
	newTags := make([]dom_tag.EmbeddedTag, 0, len(collection.Tags))
	found := false
	for _, existingTag := range collection.Tags {
		if existingTag.ID != tagID {
			newTags = append(newTags, existingTag)
		} else {
			found = true
		}
	}

	if !found {
		return nil // Tag wasn't assigned, idempotent
	}

	collection.Tags = newTags
	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)
	}

	// Remove lightweight assignment tracking
	if err := uc.tagRepo.UnassignTag(ctx, tagID, collectionID, dom_tag.EntityTypeCollection); err != nil {
		return fmt.Errorf("failed to remove tag assignment: %w", err)
	}

	return nil
}

func (uc *UnassignTagUseCase) unassignFromFile(ctx context.Context, tagID, fileID gocql.UUID) error {
	// Get file metadata
	file, err := uc.fileRepo.Get(fileID)
	if err != nil {
		return fmt.Errorf("file not found: %w", err)
	}

	// Remove tag from file's tag list
	newTags := make([]dom_tag.EmbeddedTag, 0, len(file.Tags))
	found := false
	for _, existingTag := range file.Tags {
		if existingTag.ID != tagID {
			newTags = append(newTags, existingTag)
		} else {
			found = true
		}
	}

	if !found {
		return nil // Tag wasn't assigned, idempotent
	}

	file.Tags = newTags
	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)
	}

	// Remove lightweight assignment tracking
	if err := uc.tagRepo.UnassignTag(ctx, tagID, fileID, dom_tag.EntityTypeFile); err != nil {
		return fmt.Errorf("failed to remove tag assignment: %w", err)
	}

	return nil
}
