package linguistics

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"sync"

	"tercul/internal/platform/cache"
	"tercul/internal/platform/log"
)

// Analyzer defines the interface for linguistic analysis services
type Analyzer interface {
	// AnalyzeText performs linguistic analysis on the given text
	AnalyzeText(ctx context.Context, text string, language string) (*AnalysisResult, error)

	// AnalyzeWork performs linguistic analysis on a work
	AnalyzeWork(ctx context.Context, workID uint) error
}

// BasicAnalyzer implements the Analyzer interface as a thin coordination layer.
// It delegates pure text analysis to TextAnalyzer and work analysis to WorkAnalysisService,
// and only handles caching and orchestration concerns here to preserve SRP/DRY.
type BasicAnalyzer struct {
	textAnalyzer        TextAnalyzer
	workAnalysisService WorkAnalysisService
	cache               cache.Cache
	resultCache         map[string]*AnalysisResult
	cacheMutex          sync.RWMutex
	concurrency         int
	cacheEnabled        bool
}

// NewBasicAnalyzer creates a new BasicAnalyzer
func NewBasicAnalyzer(
	textAnalyzer TextAnalyzer,
	workService WorkAnalysisService,
	redis cache.Cache,
	concurrency int,
	cacheEnabled bool,
) *BasicAnalyzer {
	if concurrency <= 0 {
		concurrency = 4
	}
	return &BasicAnalyzer{
		textAnalyzer:        textAnalyzer,
		workAnalysisService: workService,
		cache:               redis,
		resultCache:         make(map[string]*AnalysisResult),
		concurrency:         concurrency,
		cacheEnabled:        cacheEnabled,
	}
}

// WithCache adds a cache to the analyzer
func (a *BasicAnalyzer) WithCache(cache cache.Cache) *BasicAnalyzer {
	a.cache = cache
	return a
}

// WithConcurrency sets the number of concurrent workers
func (a *BasicAnalyzer) WithConcurrency(concurrency int) *BasicAnalyzer {
	if concurrency > 0 {
		a.concurrency = concurrency
	}
	return a
}

// EnableCache enables in-memory caching of analysis results
func (a *BasicAnalyzer) EnableCache() {
	a.cacheEnabled = true
}

// DisableCache disables in-memory caching of analysis results
func (a *BasicAnalyzer) DisableCache() {
	a.cacheEnabled = false
}

// AnalyzeText performs basic linguistic analysis on the given text
func (a *BasicAnalyzer) AnalyzeText(ctx context.Context, text string, language string) (*AnalysisResult, error) {
	logger := log.FromContext(ctx).With("language", language).With("textLength", len(text))
	// Check in-memory cache first if enabled
	if a.cacheEnabled {
		cacheKey := makeTextCacheKey(language, text)

		// Try to get from in-memory cache
		a.cacheMutex.RLock()
		cachedResult, found := a.resultCache[cacheKey]
		a.cacheMutex.RUnlock()

		if found {
			logger.Debug("In-memory cache hit for text analysis")
			return cachedResult, nil
		}

		// Try to get from Redis cache if available
		if a.cache != nil {
			var cachedResult AnalysisResult
			err := a.cache.Get(ctx, "text_analysis:"+cacheKey, &cachedResult)
			if err == nil {
				logger.Debug("Redis cache hit for text analysis")

				// Store in in-memory cache too
				a.cacheMutex.Lock()
				a.resultCache[cacheKey] = &cachedResult
				a.cacheMutex.Unlock()

				return &cachedResult, nil
			}
		}
	}

	// Cache miss or caching disabled, perform analysis using the pure TextAnalyzer
	logger.Debug("Performing text analysis")

	var (
		result *AnalysisResult
		err    error
	)
	if len(text) > 10000 && a.concurrency > 1 {
		result, err = a.textAnalyzer.AnalyzeTextConcurrently(ctx, text, language, a.concurrency)
	} else {
		result, err = a.textAnalyzer.AnalyzeText(ctx, text, language)
	}
	if err != nil {
		return nil, err
	}

	// Cache the result if caching is enabled
	if a.cacheEnabled {
		cacheKey := makeTextCacheKey(language, text)

		// Store in in-memory cache
		a.cacheMutex.Lock()
		a.resultCache[cacheKey] = result
		a.cacheMutex.Unlock()

		// Store in Redis cache if available
		if a.cache != nil {
			if err := a.cache.Set(ctx, "text_analysis:"+cacheKey, result, 0); err != nil {
				logger.Error(err, "Failed to cache text analysis result")
			}
		}
	}

	return result, nil
}

// AnalyzeWork performs linguistic analysis on a work and stores the results
func (a *BasicAnalyzer) AnalyzeWork(ctx context.Context, workID uint) error {
	// Delegate to the WorkAnalysisService to preserve single ownership
	return a.workAnalysisService.AnalyzeWork(ctx, workID)
}

// Helper functions for text analysis

// Note: max was unused and has been removed to keep the code minimal and focused

// makeTextCacheKey builds a stable cache key using a content hash to avoid collisions/leaks
func makeTextCacheKey(language, text string) string {
	h := sha256.Sum256([]byte(text))
	return language + ":" + hex.EncodeToString(h[:])
}
