<?php

declare(strict_types=1);

namespace App\Framework\Database\Indexing;

use App\Framework\Database\Indexing\ValueObjects\IndexRecommendation;
use App\Framework\Database\Indexing\ValueObjects\IndexType;
use App\Framework\Database\Indexing\ValueObjects\RecommendationPriority;
use App\Framework\Database\Profiling\SlowQueryDetector;
use App\Framework\Database\Profiling\SlowQueryPattern;

/**
 * Generates smart composite index recommendations based on query patterns
 *
 * Analyzes slow queries and suggests optimal composite indexes
 */
final readonly class CompositeIndexGenerator
{
    public function __construct(
        private IndexAnalyzer $analyzer,
        private IndexUsageTracker $usageTracker,
        private SlowQueryDetector $slowQueryDetector
    ) {}

    /**
     * Generate index recommendations for a table
     *
     * @return array<IndexRecommendation>
     */
    public function generateRecommendations(string $tableName): array
    {
        $recommendations = [];

        // Get slow queries affecting this table
        $slowQueries = $this->getSlowQueriesForTable($tableName);

        foreach ($slowQueries as $query) {
            $analysis = $this->analyzer->analyzeQuery($query['sql']);

            // If query uses filesort or temporary table, suggest index
            if ($analysis['using_filesort'] || $analysis['using_temporary']) {
                $recommendation = $this->analyzeQueryForIndex($query, $tableName, $analysis);
                if ($recommendation) {
                    $recommendations[] = $recommendation;
                }
            }

            // If full table scan detected
            if ($analysis['key_type'] === 'ALL' || $analysis['key_type'] === 'scan') {
                $recommendation = $this->suggestIndexForTableScan($query, $tableName, $analysis);
                if ($recommendation) {
                    $recommendations[] = $recommendation;
                }
            }
        }

        // Deduplicate and prioritize recommendations
        return $this->deduplicateRecommendations($recommendations);
    }

    /**
     * Suggest composite index based on WHERE and ORDER BY clauses
     */
    private function analyzeQueryForIndex(array $query, string $tableName, array $analysis): ?IndexRecommendation
    {
        $whereColumns = $this->extractWhereColumns($query['sql']);
        $orderByColumns = $this->extractOrderByColumns($query['sql']);

        if (empty($whereColumns) && empty($orderByColumns)) {
            return null;
        }

        // Composite index: WHERE columns first, then ORDER BY columns
        $columns = array_unique(array_merge($whereColumns, $orderByColumns));

        if (empty($columns)) {
            return null;
        }

        $estimatedSpeedup = $this->estimateSpeedup($analysis);
        $affectedQueries = $this->countAffectedQueries($columns, $tableName);

        return new IndexRecommendation(
            tableName: $tableName,
            columns: $columns,
            indexType: IndexType::BTREE,
            reason: $this->generateRecommendationReason($whereColumns, $orderByColumns, $analysis),
            priority: RecommendationPriority::fromMetrics($estimatedSpeedup, $affectedQueries),
            estimatedSpeedup: $estimatedSpeedup,
            affectedQueries: $affectedQueries
        );
    }

    /**
     * Suggest index for full table scan
     */
    private function suggestIndexForTableScan(array $query, string $tableName, array $analysis): ?IndexRecommendation
    {
        $whereColumns = $this->extractWhereColumns($query['sql']);

        if (empty($whereColumns)) {
            return null;
        }

        $estimatedSpeedup = max(10.0, $analysis['rows_examined'] / 100);
        $affectedQueries = $this->countAffectedQueries($whereColumns, $tableName);

        return new IndexRecommendation(
            tableName: $tableName,
            columns: $whereColumns,
            indexType: IndexType::BTREE,
            reason: "Full table scan detected ({$analysis['rows_examined']} rows examined)",
            priority: RecommendationPriority::fromMetrics($estimatedSpeedup, $affectedQueries),
            estimatedSpeedup: $estimatedSpeedup,
            affectedQueries: $affectedQueries
        );
    }

    /**
     * Extract columns from WHERE clause
     */
    private function extractWhereColumns(string $sql): array
    {
        $columns = [];

        // Simple regex-based extraction (can be improved with SQL parser)
        if (preg_match('/WHERE\s+(.*?)(?:ORDER BY|GROUP BY|LIMIT|$)/is', $sql, $matches)) {
            $whereClause = $matches[1];

            // Extract column names from conditions like "column = value" or "column IN (...)"
            if (preg_match_all('/(\w+)\s*(?:=|IN|>|<|>=|<=|LIKE)/i', $whereClause, $columnMatches)) {
                $columns = $columnMatches[1];
            }
        }

        return array_unique(array_filter($columns, fn($col) => !in_array(strtoupper($col), ['AND', 'OR', 'NOT'])));
    }

    /**
     * Extract columns from ORDER BY clause
     */
    private function extractOrderByColumns(string $sql): array
    {
        $columns = [];

        if (preg_match('/ORDER BY\s+(.*?)(?:LIMIT|$)/is', $sql, $matches)) {
            $orderByClause = $matches[1];

            // Extract column names, ignoring ASC/DESC
            if (preg_match_all('/(\w+)\s*(?:ASC|DESC)?/i', $orderByClause, $columnMatches)) {
                $columns = array_filter($columnMatches[1], fn($col) => $col !== 'ASC' && $col !== 'DESC');
            }
        }

        return array_unique($columns);
    }

    /**
     * Estimate query speedup from adding index
     */
    private function estimateSpeedup(array $analysis): float
    {
        $rowsExamined = $analysis['rows_examined'];

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

        // Rough speedup estimation based on rows examined
        if ($rowsExamined > 100000) {
            return 20.0;
        }

        if ($rowsExamined > 10000) {
            return 10.0;
        }

        if ($rowsExamined > 1000) {
            return 5.0;
        }

        if ($rowsExamined > 100) {
            return 2.0;
        }

        return 1.5;
    }

    /**
     * Count queries that would benefit from this index
     */
    private function countAffectedQueries(array $columns, string $tableName): int
    {
        // Simplified: count slow queries with these columns
        // In production, would analyze query log more thoroughly
        return count($this->getSlowQueriesForTable($tableName));
    }

    /**
     * Generate human-readable recommendation reason
     */
    private function generateRecommendationReason(array $whereColumns, array $orderByColumns, array $analysis): string
    {
        $reasons = [];

        if (!empty($whereColumns)) {
            $whereStr = implode(', ', $whereColumns);
            $reasons[] = "WHERE clause on columns: {$whereStr}";
        }

        if (!empty($orderByColumns)) {
            $orderStr = implode(', ', $orderByColumns);
            $reasons[] = "ORDER BY columns: {$orderStr}";
        }

        if ($analysis['using_filesort']) {
            $reasons[] = "Query uses filesort";
        }

        if ($analysis['using_temporary']) {
            $reasons[] = "Query uses temporary table";
        }

        return implode('; ', $reasons);
    }

    /**
     * Get slow queries affecting a specific table
     */
    private function getSlowQueriesForTable(string $tableName): array
    {
        // This would integrate with SlowQueryDetector
        // For now, return empty array (would need slow query log access)
        return [];
    }

    /**
     * Deduplicate recommendations and keep highest priority
     */
    private function deduplicateRecommendations(array $recommendations): array
    {
        $unique = [];

        foreach ($recommendations as $recommendation) {
            $key = $recommendation->tableName . ':' . implode(',', $recommendation->columns);

            if (!isset($unique[$key]) ||
                $this->isPriorityHigher($recommendation->priority, $unique[$key]->priority)) {
                $unique[$key] = $recommendation;
            }
        }

        // Sort by priority (CRITICAL > HIGH > MEDIUM > LOW)
        usort($unique, function(IndexRecommendation $a, IndexRecommendation $b) {
            $priorityOrder = [
                RecommendationPriority::CRITICAL->value => 4,
                RecommendationPriority::HIGH->value => 3,
                RecommendationPriority::MEDIUM->value => 2,
                RecommendationPriority::LOW->value => 1
            ];

            return ($priorityOrder[$b->priority->value] ?? 0) - ($priorityOrder[$a->priority->value] ?? 0);
        });

        return array_values($unique);
    }

    /**
     * Compare priority levels
     */
    private function isPriorityHigher(RecommendationPriority $priority1, RecommendationPriority $priority2): bool
    {
        $priorityOrder = [
            RecommendationPriority::CRITICAL->value => 4,
            RecommendationPriority::HIGH->value => 3,
            RecommendationPriority::MEDIUM->value => 2,
            RecommendationPriority::LOW->value => 1
        ];

        return ($priorityOrder[$priority1->value] ?? 0) > ($priorityOrder[$priority2->value] ?? 0);
    }
}
