package domain import ( "context" "fmt" archivalStore "git.nakama.town/fmartingr/hako/internal/archival/store" "git.nakama.town/fmartingr/hako/internal/dependencies" "git.nakama.town/fmartingr/hako/internal/model" ) // CategoryDomain handles business logic for categories type CategoryDomain struct { deps model.Dependencies } // NewCategoryDomain creates a new CategoryDomain func NewCategoryDomain(deps model.Dependencies) model.CategoryDomain { return &CategoryDomain{deps: deps} } // getDeps returns the concrete dependencies implementation func (d *CategoryDomain) getDeps() *dependencies.Dependencies { return d.deps.(*dependencies.Dependencies) } // DetermineCategoriesFromMimeType returns slice of category IDs matching the MIME type func (d *CategoryDomain) DetermineCategoriesFromMimeType(ctx context.Context, mimeType string) ([]string, error) { deps := d.getDeps() categories, err := deps.CategoryStore.GetByMimeType(ctx, mimeType) if err != nil { return nil, fmt.Errorf("failed to determine categories from MIME type: %w", err) } categoryIDs := make([]string, 0, len(categories)) for _, cat := range categories { categoryIDs = append(categoryIDs, cat.ID) } return categoryIDs, nil } // UpdateLinkCategories analyzes MIME types and updates link's categories func (d *CategoryDomain) UpdateLinkCategories(ctx context.Context, linkID string, mimeTypes []string) error { deps := d.getDeps() // Collect all unique category IDs from all MIME types categoryIDSet := make(map[string]bool) for _, mimeType := range mimeTypes { if mimeType == "" { continue } categoryIDs, err := d.DetermineCategoriesFromMimeType(ctx, mimeType) if err != nil { // Log error but continue with other MIME types deps.Logger().Error("Failed to determine categories from MIME type", "mimeType", mimeType, "error", err) continue } for _, catID := range categoryIDs { categoryIDSet[catID] = true } } // Get current categories for the link currentCategories, err := deps.LinkCategoryStore.GetCategoriesByLinkID(ctx, linkID) if err != nil { return fmt.Errorf("failed to get current categories: %w", err) } currentCategoryIDSet := make(map[string]bool) for _, cat := range currentCategories { currentCategoryIDSet[cat.ID] = true } // Add new categories for catID := range categoryIDSet { if !currentCategoryIDSet[catID] { if err := deps.LinkCategoryStore.AddCategory(ctx, linkID, catID); err != nil { return fmt.Errorf("failed to add category to link: %w", err) } } } // Remove categories that are no longer applicable (only keep categories from the latest archive) for catID := range currentCategoryIDSet { if !categoryIDSet[catID] { if err := deps.LinkCategoryStore.RemoveCategory(ctx, linkID, catID); err != nil { return fmt.Errorf("failed to remove category from link: %w", err) } } } return nil } // ListCategories retrieves all categories func (d *CategoryDomain) ListCategories(ctx context.Context) ([]*model.Category, error) { deps := d.getDeps() opts := archivalStore.CategoryListOptions{} categories, err := deps.CategoryStore.List(ctx, opts) if err != nil { return nil, fmt.Errorf("failed to list categories: %w", err) } return categories, nil }