<?php

declare(strict_types=1);

namespace App\Framework\Database\QueryOptimization\Analysis;

use App\Framework\Database\QueryOptimization\ValueObjects\QueryPattern;
use App\Framework\Database\QueryOptimization\ValueObjects\NPlusOneDetection;

/**
 * Eager Loading Analyzer
 *
 * Analyzes N+1 patterns and suggests eager loading strategies
 */
final readonly class EagerLoadingAnalyzer
{
    /**
     * Analyze N+1 detections and generate eager loading strategies
     *
     * @param array<NPlusOneDetection> $detections
     * @return array<EagerLoadingStrategy>
     */
    public function analyzeDetections(array $detections): array
    {
        $strategies = [];

        foreach ($detections as $detection) {
            $strategy = $this->createStrategy($detection);
            if ($strategy !== null) {
                $strategies[] = $strategy;
            }
        }

        // Sort by priority (critical first)
        usort($strategies, function (EagerLoadingStrategy $a, EagerLoadingStrategy $b) {
            return $b->priority <=> $a->priority;
        });

        return $strategies;
    }

    /**
     * Create eager loading strategy from N+1 detection
     */
    private function createStrategy(NPlusOneDetection $detection): ?EagerLoadingStrategy
    {
        $pattern = $detection->pattern;
        $tableName = $pattern->getTableName();

        if ($tableName === null) {
            return null;
        }

        // Determine relationship type from pattern
        $relationshipType = $this->detectRelationshipType($pattern);

        // Generate code example
        $codeExample = $this->generateCodeExample($tableName, $relationshipType);

        // Calculate priority
        $priority = $this->calculatePriority($detection);

        return new EagerLoadingStrategy(
            tableName: $tableName,
            relationshipType: $relationshipType,
            codeExample: $codeExample,
            priority: $priority,
            estimatedImprovement: $detection->getPerformanceImpact(),
            affectedLocations: $pattern->getCallerLocations()
        );
    }

    /**
     * Detect relationship type from query pattern
     */
    private function detectRelationshipType(QueryPattern $pattern): string
    {
        $firstQuery = $pattern->queries[0];
        $sql = $firstQuery->sql;

        // Check for foreign key patterns
        if (preg_match('/WHERE\s+.*_id\s*=\s*\?/i', $sql)) {
            return 'belongsTo'; // Many-to-one
        }

        // Check for pivot table patterns
        if (preg_match('/JOIN.*_.*ON/i', $sql)) {
            return 'belongsToMany'; // Many-to-many
        }

        // Default: hasMany (one-to-many)
        return 'hasMany';
    }

    /**
     * Generate code example for eager loading
     */
    private function generateCodeExample(string $tableName, string $relationshipType): string
    {
        $relationName = $this->guessRelationName($tableName);

        $example = "// Before (N+1 Problem):\n";
        $example .= "\$items = \$repository->findAll();\n";
        $example .= "foreach (\$items as \$item) {\n";
        $example .= "    \$related = \$item->{$relationName}; // Triggers separate query\n";
        $example .= "}\n\n";

        $example .= "// After (Eager Loading):\n";

        switch ($relationshipType) {
            case 'belongsTo':
                $example .= "\$items = \$repository->findWithRelations(['*'], ['{$relationName}']);\n";
                $example .= "// Or using query builder:\n";
                $example .= "\$items = \$queryBuilder\n";
                $example .= "    ->select('*')\n";
                $example .= "    ->leftJoin('{$tableName}', 'parent.{$tableName}_id', '=', '{$tableName}.id')\n";
                $example .= "    ->get();\n";
                break;

            case 'hasMany':
                $example .= "\$items = \$repository->findWithRelations(['*'], ['{$relationName}']);\n";
                $example .= "// This will load all related {$tableName} in one query\n";
                break;

            case 'belongsToMany':
                $example .= "\$items = \$repository->findWithRelations(['*'], ['{$relationName}']);\n";
                $example .= "// This will load all related {$tableName} via pivot table\n";
                break;
        }

        return $example;
    }

    /**
     * Guess relation name from table name
     */
    private function guessRelationName(string $tableName): string
    {
        // Remove trailing 's' for plurals
        $singular = rtrim($tableName, 's');

        // Convert snake_case to camelCase
        return lcfirst(str_replace('_', '', ucwords($singular, '_')));
    }

    /**
     * Calculate priority for eager loading strategy
     */
    private function calculatePriority(NPlusOneDetection $detection): int
    {
        $severity = $detection->getSeverityScore();
        $execCount = $detection->pattern->getExecutionCount();
        $totalTime = $detection->pattern->getTotalExecutionTimeMs();

        // Base priority from severity (0-10)
        $priority = $severity * 10;

        // Bonus for high execution count
        if ($execCount > 100) {
            $priority += 30;
        } elseif ($execCount > 50) {
            $priority += 20;
        } elseif ($execCount > 20) {
            $priority += 10;
        }

        // Bonus for high total time
        if ($totalTime > 1000) {
            $priority += 20;
        } elseif ($totalTime > 500) {
            $priority += 10;
        }

        return min($priority, 200); // Cap at 200
    }

    /**
     * Generate summary report of eager loading opportunities
     *
     * @param array<EagerLoadingStrategy> $strategies
     */
    public function generateReport(array $strategies): string
    {
        $report = "=== Eager Loading Opportunities ===\n\n";

        if (empty($strategies)) {
            $report .= "No eager loading opportunities detected.\n";
            return $report;
        }

        $report .= "Found " . count($strategies) . " opportunities:\n\n";

        foreach ($strategies as $index => $strategy) {
            $report .= sprintf(
                "[%d] %s (Priority: %d)\n",
                $index + 1,
                $strategy->tableName,
                $strategy->priority
            );
            $report .= "    Relationship Type: {$strategy->relationshipType}\n";
            $report .= "    Estimated Improvement: {$strategy->estimatedImprovement}\n";
            $report .= "    Affected Locations:\n";

            foreach ($strategy->affectedLocations as $location) {
                $report .= "      - {$location}\n";
            }

            $report .= "\n";
            $report .= "    Code Example:\n";
            $report .= "    " . str_replace("\n", "\n    ", $strategy->codeExample);
            $report .= "\n\n";
        }

        return $report;
    }
}

/**
 * Eager Loading Strategy Value Object
 */
final readonly class EagerLoadingStrategy
{
    public function __construct(
        public string $tableName,
        public string $relationshipType,
        public string $codeExample,
        public int $priority,
        public string $estimatedImprovement,
        public array $affectedLocations
    ) {}

    public function toArray(): array
    {
        return [
            'table_name' => $this->tableName,
            'relationship_type' => $this->relationshipType,
            'code_example' => $this->codeExample,
            'priority' => $this->priority,
            'estimated_improvement' => $this->estimatedImprovement,
            'affected_locations' => $this->affectedLocations,
        ];
    }
}
