<?php

declare(strict_types=1);

namespace App\Framework\Database\Migration\Commands;

use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ExitCode;
use App\Framework\Console\Table\ConsoleTable;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ValueObjects\SqlQuery;
use App\Framework\DateTime\Clock;
use App\Framework\Performance\ValueObjects\Byte;

final readonly class MigrationPerformanceCommand
{
    public function __construct(
        private ConnectionInterface $connection,
        private Clock $clock
    ) {
    }

    #[ConsoleCommand('db:migration:performance', 'Display performance statistics for migration executions')]
    public function showPerformance(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $output->writeln('');
        $output->writeln('<info>📊 Migration Performance Report</info>');
        $output->writeln(str_repeat('=', 60));

        try {
            // Check if performance tracking table exists
            if (! $this->performanceTableExists()) {
                $output->writeln('<warning>⚠️  No performance data available yet.</warning>');
                $output->writeln('Performance tracking will be available after running migrations.');

                return ExitCode::SUCCESS;
            }

            // Get performance statistics
            $stats = $this->getPerformanceStatistics();

            if (empty($stats['total'])) {
                $output->writeln('<comment>No migration performance data recorded yet.</comment>');

                return ExitCode::SUCCESS;
            }

            // Display summary
            $this->displaySummary($output, $stats);

            // Display recent migrations table
            $this->displayRecentMigrations($output);

            // Display top slowest migrations
            $this->displaySlowestMigrations($output);

            // Display memory usage statistics
            $this->displayMemoryStatistics($output);

            return ExitCode::SUCCESS;

        } catch (\Throwable $e) {
            $output->writeln('<error>Failed to fetch performance data: ' . $e->getMessage() . '</error>');

            return ExitCode::FAILURE;
        }
    }

    private function performanceTableExists(): bool
    {
        try {
            $this->connection->query(SqlQuery::create(
                'SELECT 1 FROM migration_performance LIMIT 1'
            ));

            return true;
        } catch (\Throwable) {
            return false;
        }
    }

    private function getPerformanceStatistics(): array
    {
        $stats = $this->connection->queryFirst(SqlQuery::create(
            'SELECT
                COUNT(*) as total,
                AVG(duration_ms) as avg_duration,
                MIN(duration_ms) as min_duration,
                MAX(duration_ms) as max_duration,
                AVG(peak_memory) as avg_memory,
                MAX(peak_memory) as max_memory,
                SUM(CASE WHEN status = "success" THEN 1 ELSE 0 END) as successful,
                SUM(CASE WHEN status = "failed" THEN 1 ELSE 0 END) as failed
            FROM migration_performance'
        ));

        return $stats ?: [
            'total' => 0,
            'avg_duration' => 0,
            'min_duration' => 0,
            'max_duration' => 0,
            'avg_memory' => 0,
            'max_memory' => 0,
            'successful' => 0,
            'failed' => 0,
        ];
    }

    private function displaySummary(ConsoleOutput $output, array $stats): void
    {
        $output->writeln('');
        $output->writeln('<comment>📈 Overall Statistics</comment>');
        $output->writeln(str_repeat('-', 40));

        $successRate = $stats['total'] > 0
            ? round(($stats['successful'] / $stats['total']) * 100, 1)
            : 0;

        $output->writeln(sprintf('Total Migrations:     <info>%d</info>', $stats['total']));
        $output->writeln(sprintf(
            'Successful:           <info>%d</info> (%.1f%%)',
            $stats['successful'],
            $successRate
        ));
        $output->writeln(sprintf('Failed:               <error>%d</error>', $stats['failed']));
        $output->writeln('');
        $output->writeln(sprintf('Average Duration:     <info>%.2f ms</info>', $stats['avg_duration']));
        $output->writeln(sprintf('Fastest Migration:    <info>%.2f ms</info>', $stats['min_duration']));
        $output->writeln(sprintf('Slowest Migration:    <warning>%.2f ms</warning>', $stats['max_duration']));
        $output->writeln('');
        $output->writeln(sprintf(
            'Average Memory:       <info>%s</info>',
            Byte::fromBytes((int)$stats['avg_memory'])->toHumanReadable()
        ));
        $output->writeln(sprintf(
            'Peak Memory:          <warning>%s</warning>',
            Byte::fromBytes((int)$stats['max_memory'])->toHumanReadable()
        ));
    }

    private function displayRecentMigrations(ConsoleOutput $output): void
    {
        $output->writeln('');
        $output->writeln('<comment>🕐 Recent Migrations (Last 10)</comment>');
        $output->writeln(str_repeat('-', 40));

        $recent = $this->connection->query(SqlQuery::create(
            'SELECT
                migration_version,
                status,
                duration_ms,
                peak_memory,
                executed_at
            FROM migration_performance
            ORDER BY executed_at DESC
            LIMIT 10'
        ));

        if ($recent->count() === 0) {
            $output->writeln('No recent migrations found.');

            return;
        }

        $table = new ConsoleTable($output);
        $table->setHeaders(['Version', 'Status', 'Duration', 'Memory', 'Executed At']);

        foreach ($recent as $row) {
            $status = $row['status'] === 'success'
                ? '<info>✅ Success</info>'
                : '<error>❌ Failed</error>';

            $table->addRow([
                $row['migration_version'],
                $status,
                sprintf('%.2f ms', $row['duration_ms']),
                Byte::fromBytes((int)$row['peak_memory'])->toHumanReadable(),
                $row['executed_at'],
            ]);
        }

        $table->render();
    }

    private function displaySlowestMigrations(ConsoleOutput $output): void
    {
        $output->writeln('');
        $output->writeln('<comment>🐌 Slowest Migrations (Top 5)</comment>');
        $output->writeln(str_repeat('-', 40));

        $slowest = $this->connection->query(SqlQuery::create(
            'SELECT
                migration_version,
                duration_ms,
                peak_memory,
                executed_at
            FROM migration_performance
            WHERE status = "success"
            ORDER BY duration_ms DESC
            LIMIT 5'
        ));

        if ($slowest->count() === 0) {
            $output->writeln('No migration data available.');

            return;
        }

        $table = new ConsoleTable($output);
        $table->setHeaders(['Version', 'Duration', 'Memory', 'Date']);

        foreach ($slowest as $row) {
            $table->addRow([
                $row['migration_version'],
                sprintf('<warning>%.2f ms</warning>', $row['duration_ms']),
                Byte::fromBytes((int)$row['peak_memory'])->toHumanReadable(),
                date('Y-m-d', strtotime($row['executed_at'])),
            ]);
        }

        $table->render();
    }

    private function displayMemoryStatistics(ConsoleOutput $output): void
    {
        $output->writeln('');
        $output->writeln('<comment>💾 Memory Usage Distribution</comment>');
        $output->writeln(str_repeat('-', 40));

        $memoryRanges = $this->connection->query(SqlQuery::create(
            'SELECT
                CASE
                    WHEN peak_memory < 1048576 THEN "< 1 MB"
                    WHEN peak_memory < 5242880 THEN "1-5 MB"
                    WHEN peak_memory < 10485760 THEN "5-10 MB"
                    WHEN peak_memory < 52428800 THEN "10-50 MB"
                    ELSE "> 50 MB"
                END as memory_range,
                COUNT(*) as count
            FROM migration_performance
            GROUP BY memory_range
            ORDER BY
                CASE memory_range
                    WHEN "< 1 MB" THEN 1
                    WHEN "1-5 MB" THEN 2
                    WHEN "5-10 MB" THEN 3
                    WHEN "10-50 MB" THEN 4
                    ELSE 5
                END'
        ));

        foreach ($memoryRanges as $range) {
            $bar = str_repeat('▓', min(50, (int)($range['count'] * 2)));
            $output->writeln(sprintf(
                '%10s: %s %d',
                $range['memory_range'],
                $bar,
                $range['count']
            ));
        }
    }

    #[ConsoleCommand('db:migration:performance:clear', 'Clear migration performance history')]
    public function clearPerformance(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        if (! $input->hasOption('force')) {
            $output->writeln('<warning>⚠️  This will delete all migration performance history.</warning>');
            $output->writeln('Use --force to confirm.');

            return ExitCode::FAILURE;
        }

        try {
            $this->connection->execute(SqlQuery::create('DELETE FROM migration_performance'));
            $output->writeln('<info>✅ Migration performance history cleared.</info>');

            return ExitCode::SUCCESS;
        } catch (\Throwable $e) {
            $output->writeln('<error>Failed to clear performance data: ' . $e->getMessage() . '</error>');

            return ExitCode::FAILURE;
        }
    }
}
