<?php

declare(strict_types=1);

namespace App\Framework\Console\Analytics\Commands;

use App\Framework\Console\Analytics\AnalyticsService;
use App\Framework\Console\Analytics\ValueObjects\Period;
use App\Framework\Console\Attributes\ConsoleCommand;
use App\Framework\Console\ExitCode;
use App\Framework\Console\Input\ConsoleInput;
use App\Framework\Console\Layout\ResponsiveOutput;
use App\Framework\Console\Output\ConsoleOutput;
use DateTimeImmutable;

final readonly class AnalyticsCommand
{
    public function __construct(
        private AnalyticsService $analyticsService
    ) {
    }

    #[ConsoleCommand(
        name: 'analytics:usage',
        description: 'Show command usage analytics and statistics'
    )]
    public function showUsage(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $commandName = $input->getArgument('command');
        $days = (int) ($input->getOption('days') ?? 30);
        $since = new DateTimeImmutable("-{$days} days");

        if ($commandName) {
            return $this->showCommandUsage($commandName, $since, $output);
        }

        return $this->showOverallUsage($since, $output);
    }

    #[ConsoleCommand(
        name: 'analytics:popular',
        description: 'Show most popular commands'
    )]
    public function showPopularCommands(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $limit = (int) ($input->getOption('limit') ?? 10);
        $days = (int) ($input->getOption('days') ?? 30);
        $since = new DateTimeImmutable("-{$days} days");

        $popularCommands = $this->analyticsService->getPopularCommands($limit, $since);

        if (empty($popularCommands)) {
            $output->writeLine('<red>No command usage data found.</red>');

            return ExitCode::SUCCESS;
        }

        $responsiveOutput = ResponsiveOutput::create($output);
        $output->writeLine("<yellow>Most Popular Commands (Last {$days} days)</yellow>\n");

        // Prepare table data
        $headers = ['Rank', 'Command', 'Executions', 'Usage %', 'Avg Time', 'Success Rate'];
        $rows = [];

        foreach ($popularCommands as $command) {
            $rows[] = [
                (string) $command->rank,
                $command->commandName,
                (string) $command->totalExecutions,
                $command->usagePercentage->format(1),
                $command->averageExecutionTime->toHumanReadable(),
                $command->successRate->format(1),
            ];
        }

        $responsiveOutput->writeTable($headers, $rows);

        return ExitCode::SUCCESS;
    }

    #[ConsoleCommand(
        name: 'analytics:trends',
        description: 'Show command usage trends over time'
    )]
    public function showTrends(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $commandName = $input->getArgument('command');
        if (! $commandName) {
            $output->writeLine('<red>Command name is required for trend analysis.</red>');

            return ExitCode::INVALID_ARGUMENTS;
        }

        $periodStr = $input->getOption('period') ?? 'daily';
        $period = match (strtolower($periodStr)) {
            'hourly' => Period::HOURLY,
            'daily' => Period::DAILY,
            'weekly' => Period::WEEKLY,
            'monthly' => Period::MONTHLY,
            'yearly' => Period::YEARLY,
            default => Period::DAILY
        };

        $days = (int) ($input->getOption('days') ?? 30);
        $start = new DateTimeImmutable("-{$days} days");
        $end = new DateTimeImmutable();

        $trends = $this->analyticsService->getCommandTrends($commandName, $period, $start, $end);

        if (empty($trends)) {
            $output->writeLine('<red>No trend data found for this command.</red>');

            return ExitCode::SUCCESS;
        }

        $responsiveOutput = ResponsiveOutput::create($output);
        $trend = $trends[0];

        $output->writeLine("<yellow>Usage Trends for '{$commandName}' ({$period->getDescription()})</yellow>\n");

        // Show trend summary
        $summary = [
            'Period' => "{$trend->startDate->format('Y-m-d')} to {$trend->endDate->format('Y-m-d')}",
            'Trend' => $trend->getTrendDescription(),
            'Direction' => ($trend->trendDirection > 0 ? '+' : '') . round($trend->trendDirection, 3),
            'Strength' => round($trend->trendStrength, 3),
        ];

        $responsiveOutput->writeKeyValue($summary);
        $output->writeLine('');

        // Show trend data points as responsive table
        $headers = ['Date', 'Executions', 'Avg Time', 'Success Rate'];
        $rows = [];

        foreach ($trend->dataPoints as $point) {
            $rows[] = [
                $point->date->format($period->getDateFormat()),
                (string) $point->executionCount,
                $point->averageExecutionTime->toHumanReadable(),
                $point->successRate->format(1),
            ];
        }

        $responsiveOutput->writeTable($headers, $rows);

        return ExitCode::SUCCESS;
    }

    #[ConsoleCommand(
        name: 'analytics:health',
        description: 'Show command health scores and problematic commands'
    )]
    public function showHealth(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $days = (int) ($input->getOption('days') ?? 30);
        $since = new DateTimeImmutable("-{$days} days");

        $output->writeLine("<yellow>Command Health Analysis (Last {$days} days)</yellow>\n");

        // Show failing commands
        $failingCommands = $this->analyticsService->getMostFailingCommands(10, $since);
        if (! empty($failingCommands)) {
            $output->writeLine("<red>Commands with Highest Failure Rates:</red>");
            $output->writeLine(sprintf(
                "%-25s %-12s %-12s %s",
                'Command',
                'Failure Rate',
                'Total Runs',
                'Failures'
            ));
            $output->writeLine(str_repeat('-', 70));

            foreach ($failingCommands as $command) {
                $output->writeLine(sprintf(
                    "%-25s %-12s %-12d %d",
                    $command['command_name'],
                    $command['failure_rate']->format(1),
                    $command['total_executions'],
                    $command['failed_executions']
                ));
            }
            $output->writeLine('');
        }

        // Show slow commands
        $slowCommands = $this->analyticsService->getSlowestCommands(10, $since);
        if (! empty($slowCommands)) {
            $output->writeLine("<yellow>Slowest Commands:</yellow>");
            $output->writeLine(sprintf(
                "%-25s %-15s %-15s %s",
                'Command',
                'Avg Time',
                'Max Time',
                'Total Runs'
            ));
            $output->writeLine(str_repeat('-', 80));

            foreach ($slowCommands as $command) {
                $output->writeLine(sprintf(
                    "%-25s %-15s %-15s %d",
                    $command['command_name'],
                    $command['average_execution_time']->toHumanReadable(),
                    $command['max_execution_time']->toHumanReadable(),
                    $command['total_executions']
                ));
            }
            $output->writeLine('');
        }

        return ExitCode::SUCCESS;
    }

    #[ConsoleCommand(
        name: 'analytics:cleanup',
        description: 'Clean up old analytics data'
    )]
    public function cleanup(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $days = (int) ($input->getOption('days') ?? 90);

        $output->writeLine("<yellow>Cleaning up analytics data older than {$days} days...</yellow>");

        $deletedRecords = $this->analyticsService->cleanupOldData($days);

        $output->writeLine("<green>Successfully deleted {$deletedRecords} old analytics records.</green>");

        return ExitCode::SUCCESS;
    }

    private function showCommandUsage(string $commandName, DateTimeImmutable $since, ConsoleOutput $output): ExitCode
    {
        $stats = $this->analyticsService->getCommandStatistics($commandName, $since);

        $output->writeLine("<yellow>Usage Statistics for '{$commandName}'</yellow>\n");

        if ($stats->totalExecutions === 0) {
            $output->writeLine('<red>No usage data found for this command.</red>');

            return ExitCode::SUCCESS;
        }

        $output->writeLine("Total Executions: {$stats->totalExecutions}");
        $output->writeLine("Successful: {$stats->successfulExecutions} ({$stats->getSuccessRate()->format(1)})");
        $output->writeLine("Failed: {$stats->failedExecutions} ({$stats->getFailureRate()->format(1)})");
        $output->writeLine('');
        $output->writeLine("Performance:");
        $output->writeLine("  Average Time: {$stats->averageExecutionTime->toHumanReadable()}");
        $output->writeLine("  Min Time: {$stats->minExecutionTime->toHumanReadable()}");
        $output->writeLine("  Max Time: {$stats->maxExecutionTime->toHumanReadable()}");

        if ($stats->medianExecutionTime) {
            $output->writeLine("  Median Time: {$stats->medianExecutionTime->toHumanReadable()}");
        }

        // Show hourly distribution
        if ($stats->hourlyDistribution) {
            $output->writeLine('');
            $output->writeLine("Usage by Hour:");
            $maxUsage = max($stats->hourlyDistribution);

            for ($hour = 0; $hour < 24; $hour++) {
                $usage = $stats->hourlyDistribution[$hour];
                $percentage = $maxUsage > 0 ? ($usage / $maxUsage) * 100 : 0;
                $bar = str_repeat('▓', (int) ($percentage / 5));

                $output->writeLine(sprintf(
                    "%02d:00 %4d %s",
                    $hour,
                    $usage,
                    $bar
                ));
            }
        }

        return ExitCode::SUCCESS;
    }

    private function showOverallUsage(DateTimeImmutable $since, ConsoleOutput $output): ExitCode
    {
        $commands = $this->analyticsService->getAllCommandNames();

        $output->writeLine("<yellow>Overall Command Usage Statistics</yellow>\n");

        if (empty($commands)) {
            $output->writeLine('<red>No command usage data found.</red>');

            return ExitCode::SUCCESS;
        }

        $totalExecutions = 0;
        $totalCommands = count($commands);

        foreach ($commands as $commandName) {
            $stats = $this->analyticsService->getCommandStatistics($commandName, $since);
            $totalExecutions += $stats->totalExecutions;
        }

        $output->writeLine("Total Commands: {$totalCommands}");
        $output->writeLine("Total Executions: {$totalExecutions}");
        $output->writeLine("Average per Command: " . round($totalExecutions / $totalCommands, 2));
        $output->writeLine('');

        // Show usage by hour
        $hourlyUsage = $this->analyticsService->getUsageByHour($since);
        $output->writeLine("Usage Distribution by Hour:");
        $maxHourly = max($hourlyUsage);

        for ($hour = 0; $hour < 24; $hour++) {
            $usage = $hourlyUsage[$hour];
            $percentage = $maxHourly > 0 ? ($usage / $maxHourly) * 100 : 0;
            $bar = str_repeat('▓', (int) ($percentage / 5));

            $output->writeLine(sprintf(
                "%02d:00 %4d %s",
                $hour,
                $usage,
                $bar
            ));
        }

        return ExitCode::SUCCESS;
    }
}
