package cache

import (
	"context"
	"fmt"
)

// CatalogCache provides caching for catalog operations
type CatalogCache struct {
	service *Service
	keys    *KeyBuilder
}

// NewCatalogCache creates a new catalog cache
func NewCatalogCache(service *Service, namespace string) *CatalogCache {
	return &CatalogCache{
		service: service,
		keys:    NewKeyBuilder(namespace),
	}
}

// GetDashboard retrieves cached dashboard data
func (cc *CatalogCache) GetDashboard(ctx context.Context, userID string, target interface{}) error {
	key := cc.keys.CatalogDashboardKey(userID)
	return cc.service.Get(ctx, key, target)
}

// SetDashboard stores dashboard data in cache
func (cc *CatalogCache) SetDashboard(ctx context.Context, userID string, data interface{}) error {
	key := cc.keys.CatalogDashboardKey(userID)
	return cc.service.Set(ctx, key, data, TTLCatalog)
}

// InvalidateDashboard removes cached dashboard data
func (cc *CatalogCache) InvalidateDashboard(ctx context.Context, userID string) error {
	key := cc.keys.CatalogDashboardKey(userID)
	return cc.service.Delete(ctx, key)
}

// GetDiscover retrieves cached discover sections
func (cc *CatalogCache) GetDiscover(ctx context.Context, genre, mediaType string, page int, target interface{}) error {
	key := cc.keys.CatalogDiscoverKey(genre, mediaType, page)
	return cc.service.Get(ctx, key, target)
}

// SetDiscover stores discover sections in cache
func (cc *CatalogCache) SetDiscover(ctx context.Context, genre, mediaType string, page int, data interface{}) error {
	key := cc.keys.CatalogDiscoverKey(genre, mediaType, page)
	return cc.service.Set(ctx, key, data, TTLCatalog)
}

// GetSearch retrieves cached search results
func (cc *CatalogCache) GetSearch(ctx context.Context, query, genre, mediaType string, target interface{}) error {
	key := cc.keys.CatalogSearchKey(query, genre, mediaType)
	return cc.service.Get(ctx, key, target)
}

// SetSearch stores search results in cache
func (cc *CatalogCache) SetSearch(ctx context.Context, query, genre, mediaType string, data interface{}) error {
	key := cc.keys.CatalogSearchKey(query, genre, mediaType)
	return cc.service.Set(ctx, key, data, TTLSearch)
}

// GetWatchLater retrieves cached watch later list
func (cc *CatalogCache) GetWatchLater(ctx context.Context, userID string, target interface{}) error {
	key := cc.keys.WatchLaterKey(userID)
	return cc.service.Get(ctx, key, target)
}

// SetWatchLater stores watch later list in cache
func (cc *CatalogCache) SetWatchLater(ctx context.Context, userID string, data interface{}) error {
	key := cc.keys.WatchLaterKey(userID)
	return cc.service.Set(ctx, key, data, TTLCatalog)
}

// InvalidateWatchLater removes cached watch later list
func (cc *CatalogCache) InvalidateWatchLater(ctx context.Context, userID string) error {
	key := cc.keys.WatchLaterKey(userID)
	return cc.service.Delete(ctx, key)
}

// GetContinueWatching retrieves cached continue watching list
func (cc *CatalogCache) GetContinueWatching(ctx context.Context, userID string, target interface{}) error {
	key := cc.keys.ContinueWatchingKey(userID)
	return cc.service.Get(ctx, key, target)
}

// SetContinueWatching stores continue watching list in cache
func (cc *CatalogCache) SetContinueWatching(ctx context.Context, userID string, data interface{}) error {
	key := cc.keys.ContinueWatchingKey(userID)
	return cc.service.Set(ctx, key, data, TTLCatalog)
}

// InvalidateContinueWatching removes cached continue watching list
func (cc *CatalogCache) InvalidateContinueWatching(ctx context.Context, userID string) error {
	key := cc.keys.ContinueWatchingKey(userID)
	return cc.service.Delete(ctx, key)
}

// InvalidateUserCatalog removes all cached catalog data for a user
func (cc *CatalogCache) InvalidateUserCatalog(ctx context.Context, userID string) error {
	keys := []string{
		cc.keys.CatalogDashboardKey(userID),
		cc.keys.WatchLaterKey(userID),
		cc.keys.ContinueWatchingKey(userID),
	}

	return cc.service.Delete(ctx, keys...)
}

// GetRecommendations retrieves cached recommendations
func (cc *CatalogCache) GetRecommendations(ctx context.Context, userID string, target interface{}) error {
	key := cc.keys.RecommendationKey(userID)
	return cc.service.Get(ctx, key, target)
}

// SetRecommendations stores recommendations in cache
func (cc *CatalogCache) SetRecommendations(ctx context.Context, userID string, data interface{}) error {
	key := cc.keys.RecommendationKey(userID)
	return cc.service.Set(ctx, key, data, TTLRecommendation)
}

// InvalidateRecommendations removes cached recommendations
func (cc *CatalogCache) InvalidateRecommendations(ctx context.Context, userID string) error {
	key := cc.keys.RecommendationKey(userID)
	return cc.service.Delete(ctx, key)
}

// WarmupCache pre-populates cache with common queries
func (cc *CatalogCache) WarmupCache(ctx context.Context, warmupFunc func(ctx context.Context) error) error {
	if warmupFunc == nil {
		return fmt.Errorf("warmup function is required")
	}

	return warmupFunc(ctx)
}
