<?php

namespace App\Helpers;

use App\Models\Category;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class SearchOptimizationHelper
{
    private const CACHE_PREFIX = 'search_optimization_';
    private const DEFAULT_TTL = 3600; // 1 hour

    /**
     * Build optimized category hierarchy with caching
     */
    public static function getCategoryHierarchy(string $locale = null): array
    {
        $locale = $locale ?? app()->getLocale();
        $cacheKey = self::CACHE_PREFIX . "category_hierarchy_{$locale}";

        return Cache::remember($cacheKey, self::DEFAULT_TTL, function () use ($locale) {
            $categories = Category::where('type', 'App\Models\Tag')
                ->with(['translations' => function ($query) use ($locale) {
                    $query->where('locale', $locale);
                }])
                ->get();

            return self::buildHierarchyRecursive($categories);
        });
    }

    /**
     * Get expanded category IDs with caching
     */
    public static function getExpandedCategoryIds(array $categoryIds): array
    {
        $cacheKey = self::CACHE_PREFIX . 'expanded_' . md5(serialize($categoryIds));

        return Cache::remember($cacheKey, self::DEFAULT_TTL, function () use ($categoryIds) {
            $allIds = [];

            foreach ($categoryIds as $categoryId) {
                if (is_numeric($categoryId) && $categoryId > 0) {
                    $descendants = self::getCategoryDescendants((int)$categoryId);
                    $allIds = array_merge($allIds, $descendants);
                }
            }

            return array_values(array_unique($allIds));
        });
    }

    /**
     * Get category names by IDs with caching
     */
    public static function getCategoryNamesByIds(array $categoryIds, string $locale = null): array
    {
        $locale = $locale ?? app()->getLocale();
        $cacheKey = self::CACHE_PREFIX . "names_{$locale}_" . md5(serialize($categoryIds));

        return Cache::remember($cacheKey, 300, function () use ($categoryIds, $locale) {
            $categories = Category::whereIn('id', $categoryIds)
                ->with(['translations' => function ($query) use ($locale) {
                    $query->where('locale', $locale);
                }])
                ->get();

            return $categories->map(function ($category) {
                return $category->translation ? $category->translation->name : null;
            })->filter()->values()->toArray();
        });
    }

    /**
     * Clear all search-related caches using app locales
     */
    public static function clearSearchCaches(): void
    {
        // Use your supported locales
        $locales = ['en', 'nl', 'fr', 'es', 'de'];

        foreach ($locales as $locale) {
            Cache::forget(self::CACHE_PREFIX . "category_hierarchy_{$locale}");
        }

        // Clear pattern-based cache keys
        if (Cache::getStore() instanceof \Illuminate\Cache\RedisStore) {
            $cacheKeys = Cache::getRedis()->keys(self::CACHE_PREFIX . '*');
            if (!empty($cacheKeys)) {
                Cache::getRedis()->del($cacheKeys);
            }
        }

        Log::info('Search caches cleared');
    }

    /**
     * Build search query suggestions based on category names
     */
    public static function buildSearchSuggestions(array $categoryNames, string $locale = null): array
    {
        $locale = $locale ?? app()->getLocale();
        $suggestions = [];

        foreach ($categoryNames as $categoryName) {
            // Add exact match
            $suggestions[] = [
                'text' => $categoryName,
                'weight' => 10,
                'input' => [$categoryName],
            ];

            // Add partial matches
            $words = explode(' ', $categoryName);
            if (count($words) > 1) {
                foreach ($words as $word) {
                    if (strlen($word) > 2) {
                        $suggestions[] = [
                            'text' => $word,
                            'weight' => 5,
                            'input' => [$word],
                        ];
                    }
                }
            }
        }

        return array_unique($suggestions, SORT_REGULAR);
    }

    /**
     * Optimize search results by applying relevance scoring including location
     */
    public static function optimizeSearchResults(array $results, array $searchTerms = [], array $userLocation = []): array
    {
        $optimized = [];

        foreach ($results as $result) {
            $score = $result['score'] ?? 1;

            // Boost based on model type
            $modelBoost = self::getModelTypeBoost($result['model'] ?? '');
            $score *= $modelBoost;

            // Boost based on highlight quality
            $highlightBoost = self::getHighlightBoost($result['highlight'] ?? []);
            $score *= $highlightBoost;

            // Boost based on data completeness
            $completenessBoost = self::getCompletenessBoost($result);
            $score *= $completenessBoost;

            // Boost based on recency
            $recencyBoost = self::getRecencyBoost($result);
            $score *= $recencyBoost;

            // Boost based on location proximity
            $locationBoost = self::getLocationBoost($result, $userLocation);
            $score *= $locationBoost;

            $result['optimized_score'] = $score;
            $optimized[] = $result;
        }

        // Sort by optimized score primarily, with location as a tie-breaker
        usort($optimized, function ($a, $b) {
            // Primary sort: optimized score (higher scores first)
            $aScore = $a['optimized_score'] ?? 0;
            $bScore = $b['optimized_score'] ?? 0;

            $scoreDiff = $bScore <=> $aScore;
            if ($scoreDiff !== 0) {
                return $scoreDiff;
            }

            // Secondary sort: location distance (closer locations first)
            $aDistance = $a['location_proximity']['distance'] ?? 9;
            $bDistance = $b['location_proximity']['distance'] ?? 9;
            return $aDistance <=> $bDistance;
        });

        return $optimized;
    }

    /**
     * Generate search analytics data
     */
    public static function trackSearchAnalytics(array $categoryIds, int $totalResults, float $executionTime): void
    {
        $analyticsData = [
            'timestamp' => now(),
            'category_ids' => $categoryIds,
            'total_results' => $totalResults,
            'execution_time' => $executionTime,
            'locale' => app()->getLocale(),
            'user_id' => auth()->id(),
        ];

        // Store in cache for recent searches
        $recentSearchesKey = self::CACHE_PREFIX . 'recent_searches_' . (auth()->id() ?? 'guest');
        $recentSearches = Cache::get($recentSearchesKey, []);

        array_unshift($recentSearches, $analyticsData);
        $recentSearches = array_slice($recentSearches, 0, 10); // Keep last 10 searches

        Cache::put($recentSearchesKey, $recentSearches, now()->addHours(24));

        // Log for analysis
        Log::info('Search performed', $analyticsData);
    }

    /**
     * Get search performance metrics
     */
    public static function getSearchMetrics(): array
    {
        $userId = auth()->id();
        $recentSearchesKey = self::CACHE_PREFIX . 'recent_searches_' . ($userId ?? 'guest');
        $recentSearches = Cache::get($recentSearchesKey, []);

        if (empty($recentSearches)) {
            return [
                'total_searches' => 0,
                'avg_results' => 0,
                'avg_execution_time' => 0,
                'popular_categories' => [],
            ];
        }

        $totalSearches = count($recentSearches);
        $totalResults = array_sum(array_column($recentSearches, 'total_results'));
        $totalExecutionTime = array_sum(array_column($recentSearches, 'execution_time'));

        // Get popular categories
        $allCategories = [];
        foreach ($recentSearches as $search) {
            $allCategories = array_merge($allCategories, $search['category_ids']);
        }
        $popularCategories = array_count_values($allCategories);
        arsort($popularCategories);

        return [
            'total_searches' => $totalSearches,
            'avg_results' => $totalSearches > 0 ? round($totalResults / $totalSearches, 2) : 0,
            'avg_execution_time' => $totalSearches > 0 ? round($totalExecutionTime / $totalSearches, 4) : 0,
            'popular_categories' => array_slice($popularCategories, 0, 5, true),
        ];
    }

    /**
     * Build hierarchy recursively
     */
    private static function buildHierarchyRecursive($categories, $parentId = null): array
    {
        $hierarchy = [];

        foreach ($categories as $category) {
            if ($category->parent_id == $parentId) {
                $categoryData = [
                    'id' => $category->id,
                    'name' => $category->translation->name ?? $category->name,
                    'color' => $category->relatedColor,
                    'parent_id' => $category->parent_id,
                    'children' => self::buildHierarchyRecursive($categories, $category->id)
                ];
                $hierarchy[] = $categoryData;
            }
        }

        return $hierarchy;
    }

    /**
     * Get category descendants with caching
     */
    private static function getCategoryDescendants(int $categoryId): array
    {
        $cacheKey = self::CACHE_PREFIX . "descendants_{$categoryId}";

        return Cache::remember($cacheKey, self::DEFAULT_TTL, function () use ($categoryId) {
            $category = Category::find($categoryId);
            if (!$category) {
                return [$categoryId];
            }

            try {
                $descendants = $category->descendants()->pluck('id')->toArray();
                return array_merge([$categoryId], array_map('intval', $descendants));
            } catch (\Exception $e) {
                Log::warning("Failed to get descendants for category {$categoryId}: " . $e->getMessage());
                return [$categoryId];
            }
        });
    }

    /**
     * Get model type boost factor using timebank-cc configuration
     */
    private static function getModelTypeBoost(string $modelClass): float
    {
        $type = strtolower(class_basename($modelClass));
        return timebank_config('main_search_bar.boosted_models.' . $type, 1.0);
    }

    /**
     * Get highlight quality boost
     */
    private static function getHighlightBoost(array $highlights): float
    {
        if (empty($highlights)) {
            return 1.0;
        }

        $totalHighlights = 0;
        foreach ($highlights as $fieldHighlights) {
            $totalHighlights += count($fieldHighlights);
        }

        // More highlights = higher relevance
        return 1.0 + (min($totalHighlights, 10) * 0.05);
    }

    /**
     * Get data completeness boost
     */
    private static function getCompletenessBoost(array $result): float
    {
        $completenessFactors = 0;
        $totalFactors = 0;

        // Check various completeness factors
        $checks = [
            'about' => !empty($result['about'] ?? ''),
            'about_short' => !empty($result['about_short'] ?? ''),
            'subtitle' => !empty($result['subtitle'] ?? ''),
            'photo' => !empty($result['photo'] ?? ''),
            'location' => !empty($result['location'] ?? ''),
            'category' => !empty($result['category'] ?? ''),
        ];

        foreach ($checks as $factor => $isPresent) {
            $totalFactors++;
            if ($isPresent) {
                $completenessFactors++;
            }
        }

        if ($totalFactors === 0) {
            return 1.0;
        }

        $completenessRatio = $completenessFactors / $totalFactors;
        return 1.0 + ($completenessRatio * 0.2); // Up to 20% boost
    }

    /**
     * Get location-based boost based on proximity to user
     */
    private static function getLocationBoost(array $result, array $userLocation): float
    {
        if (empty($userLocation) || !isset($result['location_proximity'])) {
            return 1.0;
        }

        $proximity = $result['location_proximity'];

        // Location proximity boost factors
        $locationBoosts = [
            'same_district' => 1.1,
            'same_city' => 1.05,
            'same_division' => 1.02,
            'same_country' => 1.0,
            'different_country' => 1.0,
            'no_location' => 1.0,
            'unknown' => 1.0,
            'error' => 1.0,
        ];

        return $locationBoosts[$proximity['level'] ?? 'unknown'] ?? 1.0;
    }

    /**
     * Get recency boost based on creation/update time
     */
    private static function getRecencyBoost(array $result): float
    {
        // This would need to be implemented based on your specific timestamp fields
        // For now, return neutral boost
        return 1.0;
    }

    /**
     * Generate location-aware search analytics
     */
    public static function trackLocationSearchAnalytics(array $categoryIds, int $totalResults, float $executionTime, array $locationHierarchy = []): void
    {
        $analyticsData = [
            'timestamp' => now(),
            'category_ids' => $categoryIds,
            'total_results' => $totalResults,
            'execution_time' => $executionTime,
            'locale' => app()->getLocale(),
            'user_id' => auth()->id(),
            'location_hierarchy' => [
                'country_id' => $locationHierarchy['country']->id ?? null,
                'division_id' => $locationHierarchy['division']->id ?? null,
                'city_id' => $locationHierarchy['city']->id ?? null,
                'district_id' => $locationHierarchy['district']->id ?? null,
            ],
        ];

        // Store in cache for recent searches
        $recentSearchesKey = self::CACHE_PREFIX . 'recent_searches_' . (auth()->id() ?? 'guest');
        $recentSearches = Cache::get($recentSearchesKey, []);

        array_unshift($recentSearches, $analyticsData);
        $recentSearches = array_slice($recentSearches, 0, 10); // Keep last 10 searches

        Cache::put($recentSearchesKey, $recentSearches, now()->addHours(24));

        // Log for analysis
        Log::info('Location-aware search performed', $analyticsData);
    }

    /**
     * Validate search parameters using timebank-cc configuration
     */
    public static function validateSearchParams(array $params): array
    {
        $validated = [];

        // Validate category IDs
        if (isset($params['category_ids']) && is_array($params['category_ids'])) {
            $validated['category_ids'] = array_filter($params['category_ids'], function ($id) {
                return is_numeric($id) && $id > 0;
            });
        }

        // Validate locale using your base language and supported locales
        if (isset($params['locale'])) {
            // Use your supported locales (en, nl, fr, es, de from your config)
            $availableLocales = ['en', 'nl', 'fr', 'es', 'de'];
            $validated['locale'] = in_array($params['locale'], $availableLocales)
                ? $params['locale']
                : timebank_config('base_language', 'en');
        }

        // Validate limit using your max_results
        if (isset($params['limit'])) {
            $maxResults = timebank_config('main_search_bar.search.max_results', 50);
            $validated['limit'] = max(1, min((int)$params['limit'], $maxResults));
        }

        return $validated;
    }

    /**
     * Build cache key for search results compatible with timebank-cc naming
     */
    public static function buildSearchCacheKey(array $params): string
    {
        ksort($params); // Ensure consistent ordering
        return 'category_search_results_' . md5(serialize($params));
    }
}
