<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Tools\Categories\Database;

use App\Framework\Core\PathProvider;
use App\Framework\Database\Migration\MigrationLoader;
use App\Framework\Mcp\McpTool;

/**
 * Database Migration Analysis and Generation Tools
 *
 * Provides intelligent migration dependency analysis, generation helpers,
 * and rollback safety checks.
 */
final readonly class MigrationAnalysisTools
{
    public function __construct(
        private PathProvider $pathProvider,
        private MigrationLoader $migrationLoader
    ) {
    }

    #[McpTool(
        name: 'analyze_migration_dependencies',
        description: 'Detect migration dependencies and suggest optimal execution order'
    )]
    public function analyzeMigrationDependencies(): array
    {
        $migrations = $this->discoverMigrations();
        $dependencies = $this->extractDependencies($migrations);
        $order = $this->calculateExecutionOrder($migrations, $dependencies);

        return [
            'total_migrations' => count($migrations),
            'migrations_with_dependencies' => count($dependencies),
            'dependency_graph' => $dependencies,
            'suggested_execution_order' => $order,
            'circular_dependencies' => $this->detectCircularDependencies($dependencies),
            'orphaned_migrations' => $this->findOrphanedMigrations($migrations, $dependencies),
            'warnings' => $this->generateDependencyWarnings($dependencies),
        ];
    }

    #[McpTool(
        name: 'migration_rollback_safety',
        description: 'Analyze rollback safety of migrations and detect potential data loss'
    )]
    public function analyzeRollbackSafety(?string $migrationName = null): array
    {
        $migrations = $migrationName
            ? [$this->findMigration($migrationName)]
            : $this->discoverMigrations();

        $analysis = [];
        foreach ($migrations as $migration) {
            $analysis[] = $this->analyzeSingleMigrationSafety($migration);
        }

        return [
            'migrations_analyzed' => count($migrations),
            'safe_rollbacks' => count(array_filter($analysis, fn ($a) => $a['safe'])),
            'unsafe_rollbacks' => count(array_filter($analysis, fn ($a) => ! $a['safe'])),
            'analysis' => $analysis,
            'recommendations' => $this->generateSafetyRecommendations($analysis),
        ];
    }

    #[McpTool(
        name: 'generate_migration_template',
        description: 'Generate migration code template from table specification'
    )]
    public function generateMigrationTemplate(string $tableName, string $action = 'create'): array
    {
        $timestamp = date('Y_m_d_His');
        $className = $this->generateClassName($action, $tableName);
        $fileName = "{$timestamp}_{$className}.php";

        $template = $this->buildMigrationTemplate($className, $tableName, $action);

        return [
            'file_name' => $fileName,
            'class_name' => $className,
            'table_name' => $tableName,
            'action' => $action,
            'template_code' => $template,
            'suggested_location' => $this->getSuggestedLocation($tableName),
            'next_steps' => $this->getNextSteps($action, $tableName),
        ];
    }

    #[McpTool(
        name: 'detect_schema_changes',
        description: 'Detect schema changes that need migrations by comparing code and database'
    )]
    public function detectSchemaChanges(): array
    {
        // This would require database introspection
        // For now, analyze migration files for patterns

        $migrations = $this->discoverMigrations();
        $tables = $this->extractTableNames($migrations);
        $changes = $this->analyzeRecentChanges($migrations);

        return [
            'total_tables' => count($tables),
            'tables' => $tables,
            'recent_changes' => $changes,
            'pending_migrations' => $this->findPendingMigrations(),
            'schema_drift_detected' => false, // Would need DB connection
            'recommendations' => $this->generateSchemaRecommendations($changes),
        ];
    }

    #[McpTool(
        name: 'migration_impact_analysis',
        description: 'Analyze impact of running/rolling back specific migration'
    )]
    public function analyzeMigrationImpact(string $migrationName, string $direction = 'up'): array
    {
        $migration = $this->findMigration($migrationName);
        if (! $migration) {
            return ['error' => "Migration {$migrationName} not found"];
        }

        $content = $this->loadMigrationContent($migration['path']);
        $impact = $this->analyzeContent($content, $direction);

        return [
            'migration' => $migrationName,
            'direction' => $direction,
            'estimated_impact' => $impact['level'],
            'affected_tables' => $impact['tables'],
            'operations' => $impact['operations'],
            'data_loss_risk' => $impact['data_loss_risk'],
            'breaking_changes' => $impact['breaking_changes'],
            'rollback_complexity' => $impact['rollback_complexity'],
            'recommendations' => $impact['recommendations'],
        ];
    }

    private function discoverMigrations(): array
    {
        $basePath = $this->pathProvider->getBasePath();
        $migrationDirs = [
            $basePath . '/src/Domain/*/Migrations',
            $basePath . '/src/Framework/*/Migrations',
        ];

        $migrations = [];
        foreach ($migrationDirs as $pattern) {
            foreach (glob($pattern) as $dir) {
                foreach (glob($dir . '/*.php') as $file) {
                    $migrations[] = [
                        'path' => $file,
                        'name' => basename($file, '.php'),
                        'directory' => dirname($file),
                        'domain' => $this->extractDomain($file),
                    ];
                }
            }
        }

        return $migrations;
    }

    private function extractDomain(string $path): string
    {
        if (preg_match('#/Domain/([^/]+)/#', $path, $matches)) {
            return $matches[1];
        }
        if (preg_match('#/Framework/([^/]+)/#', $path, $matches)) {
            return 'Framework/' . $matches[1];
        }

        return 'Unknown';
    }

    private function extractDependencies(array $migrations): array
    {
        $dependencies = [];

        foreach ($migrations as $migration) {
            $content = $this->loadMigrationContent($migration['path']);

            // Look for foreign key references
            if (preg_match_all('/->foreign\([\'"](\w+)[\'"]\)->references/', $content, $matches)) {
                foreach ($matches[1] as $referencedTable) {
                    $dependencies[$migration['name']][] = $referencedTable;
                }
            }

            // Look for explicit dependencies in comments
            if (preg_match_all('/@depends\s+(\w+)/', $content, $matches)) {
                foreach ($matches[1] as $dependency) {
                    $dependencies[$migration['name']][] = $dependency;
                }
            }
        }

        return $dependencies;
    }

    private function calculateExecutionOrder(array $migrations, array $dependencies): array
    {
        // Simple topological sort
        $ordered = [];
        $visited = [];

        $visit = function ($migration) use (&$visit, &$ordered, &$visited, $dependencies, $migrations) {
            if (isset($visited[$migration])) {
                return;
            }

            $visited[$migration] = true;

            if (isset($dependencies[$migration])) {
                foreach ($dependencies[$migration] as $dep) {
                    $visit($dep);
                }
            }

            $ordered[] = $migration;
        };

        foreach ($migrations as $migration) {
            $visit($migration['name']);
        }

        return $ordered;
    }

    private function detectCircularDependencies(array $dependencies): array
    {
        $circular = [];
        $visiting = [];
        $visited = [];

        $detectCycle = function ($node, $path) use (&$detectCycle, &$circular, &$visiting, &$visited, $dependencies) {
            if (isset($visiting[$node])) {
                $circular[] = $path;

                return;
            }

            if (isset($visited[$node])) {
                return;
            }

            $visiting[$node] = true;
            $path[] = $node;

            if (isset($dependencies[$node])) {
                foreach ($dependencies[$node] as $dep) {
                    $detectCycle($dep, $path);
                }
            }

            unset($visiting[$node]);
            $visited[$node] = true;
        };

        foreach (array_keys($dependencies) as $node) {
            $detectCycle($node, []);
        }

        return $circular;
    }

    private function findOrphanedMigrations(array $migrations, array $dependencies): array
    {
        $referenced = [];
        foreach ($dependencies as $deps) {
            foreach ($deps as $dep) {
                $referenced[$dep] = true;
            }
        }

        $orphaned = [];
        foreach ($migrations as $migration) {
            if (! isset($dependencies[$migration['name']]) && ! isset($referenced[$migration['name']])) {
                $orphaned[] = $migration['name'];
            }
        }

        return $orphaned;
    }

    private function generateDependencyWarnings(array $dependencies): array
    {
        $warnings = [];

        foreach ($dependencies as $migration => $deps) {
            if (count($deps) > 5) {
                $warnings[] = "Migration {$migration} has many dependencies ({count($deps)}). Consider breaking it up.";
            }
        }

        return $warnings;
    }

    private function findMigration(string $name): ?array
    {
        $migrations = $this->discoverMigrations();
        foreach ($migrations as $migration) {
            if ($migration['name'] === $name || str_contains($migration['name'], $name)) {
                return $migration;
            }
        }

        return null;
    }

    private function analyzeSingleMigrationSafety(array $migration): array
    {
        $content = $this->loadMigrationContent($migration['path']);

        $safe = true;
        $risks = [];

        // Check for data loss operations in down()
        if (preg_match('/function\s+down\s*\([^)]*\)[^{]*\{([^}]+)\}/s', $content, $matches)) {
            $downMethod = $matches[1];

            if (str_contains($downMethod, '->dropColumn(')) {
                $safe = false;
                $risks[] = 'Column drop in rollback - potential data loss';
            }

            if (str_contains($downMethod, '->drop(')) {
                $safe = false;
                $risks[] = 'Table drop in rollback - data loss';
            }

            if (str_contains($downMethod, '->dropForeign(')) {
                $risks[] = 'Foreign key drop - check referential integrity';
            }
        }

        return [
            'migration' => $migration['name'],
            'safe' => $safe,
            'risks' => $risks,
            'has_down_method' => str_contains($content, 'function down'),
            'complexity' => $this->calculateComplexity($content),
        ];
    }

    private function generateSafetyRecommendations(array $analysis): array
    {
        $recommendations = [];

        $unsafe = array_filter($analysis, fn ($a) => ! $a['safe']);
        if (! empty($unsafe)) {
            $recommendations[] = 'Backup database before running unsafe migrations';
            $recommendations[] = 'Test rollback in development environment first';
        }

        $noDown = array_filter($analysis, fn ($a) => ! $a['has_down_method']);
        if (! empty($noDown)) {
            $recommendations[] = 'Some migrations lack down() methods - rollback not possible';
        }

        return $recommendations;
    }

    private function generateClassName(string $action, string $tableName): string
    {
        $action = ucfirst($action);
        $table = str_replace('_', '', ucwords($tableName, '_'));

        return "{$action}{$table}Table";
    }

    private function buildMigrationTemplate(string $className, string $tableName, string $action): string
    {
        $upMethod = $this->generateUpMethod($tableName, $action);
        $downMethod = $this->generateDownMethod($tableName, $action);

        return <<<PHP
<?php

declare(strict_types=1);

namespace App\Domain\YourDomain\Migrations;

use App\Framework\Database\Migration\Migration;
use App\Framework\Database\Schema\Blueprint;
use App\Framework\Database\Schema\Schema;

final class {$className} implements Migration
{
    public function up(Schema \$schema): void
    {
{$upMethod}
    }

    public function down(Schema \$schema): void
    {
{$downMethod}
    }
}
PHP;
    }

    private function generateUpMethod(string $tableName, string $action): string
    {
        return match($action) {
            'create' => <<<PHP
        \$schema->create('{$tableName}', function (Blueprint \$table) {
            \$table->id();
            // Add your columns here
            \$table->timestamps();
        });
PHP,
            'alter' => <<<PHP
        \$schema->table('{$tableName}', function (Blueprint \$table) {
            // Add your column modifications here
        });
PHP,
            'drop' => <<<PHP
        \$schema->drop('{$tableName}');
PHP,
            default => "        // Implement your migration here"
        };
    }

    private function generateDownMethod(string $tableName, string $action): string
    {
        return match($action) {
            'create' => <<<PHP
        \$schema->drop('{$tableName}');
PHP,
            'alter' => <<<PHP
        \$schema->table('{$tableName}', function (Blueprint \$table) {
            // Reverse your changes here
        });
PHP,
            'drop' => <<<PHP
        \$schema->create('{$tableName}', function (Blueprint \$table) {
            // Recreate table structure
        });
PHP,
            default => "        // Implement rollback here"
        };
    }

    private function getSuggestedLocation(string $tableName): string
    {
        // Try to infer domain from table name
        $domain = 'YourDomain';

        if (str_contains($tableName, 'user')) {
            $domain = 'User';
        }
        if (str_contains($tableName, 'order')) {
            $domain = 'Order';
        }
        if (str_contains($tableName, 'product')) {
            $domain = 'Product';
        }

        return "src/Domain/{$domain}/Migrations/";
    }

    private function getNextSteps(string $action, string $tableName): array
    {
        return [
            "1. Save template to suggested location",
            "2. Customize the {$action} migration for {$tableName}",
            "3. Run: php console.php db:migrate",
            "4. Test rollback: php console.php db:rollback",
        ];
    }

    private function extractTableNames(array $migrations): array
    {
        $tables = [];

        foreach ($migrations as $migration) {
            $content = $this->loadMigrationContent($migration['path']);

            // Extract table names from create/table calls
            if (preg_match_all('/->(?:create|table)\([\'"](\w+)[\'"]/', $content, $matches)) {
                foreach ($matches[1] as $table) {
                    $tables[$table] = ($tables[$table] ?? 0) + 1;
                }
            }
        }

        return $tables;
    }

    private function analyzeRecentChanges(array $migrations): array
    {
        // Sort by modification time
        usort($migrations, function ($a, $b) {
            return filemtime($b['path']) <=> filemtime($a['path']);
        });

        return array_slice(array_map(function ($m) {
            return [
                'name' => $m['name'],
                'domain' => $m['domain'],
                'modified' => date('Y-m-d H:i:s', filemtime($m['path'])),
            ];
        }, $migrations), 0, 10);
    }

    private function findPendingMigrations(): array
    {
        // Would need database connection to check
        // For now, return empty
        return [];
    }

    private function generateSchemaRecommendations(array $changes): array
    {
        $recommendations = [];

        if (count($changes) > 10) {
            $recommendations[] = 'Many recent migrations - consider consolidating schema changes';
        }

        return $recommendations;
    }

    private function loadMigrationContent(string $path): string
    {
        return file_get_contents($path);
    }

    private function analyzeContent(string $content, string $direction): array
    {
        $method = $direction === 'up' ? 'up' : 'down';

        // Extract method content
        preg_match("/function\s+{$method}\s*\([^)]*\)[^{]*\{([^}]+)\}/s", $content, $matches);
        $methodContent = $matches[1] ?? '';

        $operations = [];
        $tables = [];
        $dataLossRisk = false;
        $breakingChanges = [];

        // Detect operations
        if (str_contains($methodContent, '->create(')) {
            $operations[] = 'create_table';
            preg_match_all('/->create\([\'"](\w+)[\'"]/', $methodContent, $matches);
            $tables = array_merge($tables, $matches[1]);
        }

        if (str_contains($methodContent, '->drop(')) {
            $operations[] = 'drop_table';
            $dataLossRisk = true;
            preg_match_all('/->drop\([\'"](\w+)[\'"]/', $methodContent, $matches);
            $tables = array_merge($tables, $matches[1]);
        }

        if (str_contains($methodContent, '->dropColumn(')) {
            $operations[] = 'drop_column';
            $dataLossRisk = true;
            $breakingChanges[] = 'Column removal';
        }

        if (str_contains($methodContent, '->addColumn(') || str_contains($methodContent, '->string(')) {
            $operations[] = 'add_column';
        }

        $level = $this->estimateImpactLevel($operations, $dataLossRisk);

        return [
            'level' => $level,
            'tables' => array_unique($tables),
            'operations' => $operations,
            'data_loss_risk' => $dataLossRisk,
            'breaking_changes' => $breakingChanges,
            'rollback_complexity' => $this->calculateComplexity($methodContent),
            'recommendations' => $this->generateImpactRecommendations($level, $dataLossRisk),
        ];
    }

    private function estimateImpactLevel(array $operations, bool $dataLossRisk): string
    {
        if ($dataLossRisk) {
            return 'high';
        }
        if (in_array('create_table', $operations)) {
            return 'medium';
        }
        if (in_array('add_column', $operations)) {
            return 'low';
        }

        return 'minimal';
    }

    private function calculateComplexity(string $content): string
    {
        $lines = substr_count($content, "\n");

        if ($lines > 100) {
            return 'high';
        }
        if ($lines > 50) {
            return 'medium';
        }

        return 'low';
    }

    private function generateImpactRecommendations(string $level, bool $dataLossRisk): array
    {
        $recommendations = [];

        if ($level === 'high') {
            $recommendations[] = 'High impact migration - backup database before proceeding';
            $recommendations[] = 'Test in staging environment first';
        }

        if ($dataLossRisk) {
            $recommendations[] = 'Data loss risk detected - ensure you have backups';
            $recommendations[] = 'Consider data migration strategy before running';
        }

        return $recommendations;
    }
}
