<?php

declare(strict_types=1);

namespace App\Framework\Console\Security\Commands;

use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ExitCode;
use App\Framework\Console\Input\ConsoleInput;
use App\Framework\Console\Layout\ResponsiveOutput;
use App\Framework\Console\Output\ConsoleOutput;

final readonly class DependencyAuditCommand
{
    #[ConsoleCommand(
        name: 'security:audit',
        description: 'Audit dependencies for known security vulnerabilities'
    )]
    public function audit(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $format = $input->getOption('format') ?? 'table';
        $noDevDeps = $input->hasOption('no-dev');
        $failOnVulnerabilities = $input->hasOption('fail-on-vulnerabilities');

        $output->writeLine("<yellow>Running Security Audit...</yellow>\n");

        // Build composer audit command
        $command = 'composer audit --format=' . escapeshellarg($format);
        if ($noDevDeps) {
            $command .= ' --no-dev';
        }

        // Execute composer audit
        $startTime = microtime(true);
        exec($command . ' 2>&1', $outputLines, $returnCode);
        $duration = microtime(true) - $startTime;

        $outputText = implode("\n", $outputLines);

        // Parse JSON output if format is JSON
        if ($format === 'json') {
            $auditData = json_decode($outputText, true);

            if (json_last_error() === JSON_ERROR_NONE) {
                $this->displayJsonResults($auditData, $output);

                return $this->determineExitCode(
                    $auditData,
                    $failOnVulnerabilities
                );
            }
        }

        // Display raw output for non-JSON formats
        $output->writeLine($outputText);
        $output->writeLine("\n<gray>Audit completed in " . number_format($duration, 2) . "s</gray>");

        // Return appropriate exit code
        if ($returnCode === 0) {
            $output->writeLine("\n<green>✓ No security vulnerabilities found!</green>");
            return ExitCode::SUCCESS;
        }

        $output->writeLine("\n<red>✗ Security vulnerabilities detected!</red>");
        return $failOnVulnerabilities ? ExitCode::FAILURE : ExitCode::SUCCESS;
    }

    #[ConsoleCommand(
        name: 'security:audit-summary',
        description: 'Show summary of dependency security audit'
    )]
    public function auditSummary(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $output->writeLine("<yellow>Security Audit Summary</yellow>\n");

        // Execute composer audit with JSON format
        exec('composer audit --format=json 2>&1', $outputLines, $returnCode);
        $auditData = json_decode(implode("\n", $outputLines), true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            $output->writeLine('<red>Failed to parse audit results</red>');
            return ExitCode::FAILURE;
        }

        $responsiveOutput = ResponsiveOutput::create($output);

        // Count vulnerabilities by severity
        $severityCounts = [
            'critical' => 0,
            'high' => 0,
            'medium' => 0,
            'low' => 0,
        ];

        $totalVulnerabilities = 0;
        $affectedPackages = [];

        foreach ($auditData['advisories'] ?? [] as $packageName => $advisories) {
            $affectedPackages[] = $packageName;

            foreach ($advisories as $advisory) {
                $severity = strtolower($advisory['severity'] ?? 'unknown');
                if (isset($severityCounts[$severity])) {
                    $severityCounts[$severity]++;
                }
                $totalVulnerabilities++;
            }
        }

        // Display summary
        $summary = [
            'Total Vulnerabilities' => $totalVulnerabilities,
            'Critical' => "<red>{$severityCounts['critical']}</red>",
            'High' => "<yellow>{$severityCounts['high']}</yellow>",
            'Medium' => "<cyan>{$severityCounts['medium']}</cyan>",
            'Low' => "<gray>{$severityCounts['low']}</gray>",
            'Affected Packages' => count($affectedPackages),
            'Abandoned Packages' => count($auditData['abandoned'] ?? []),
        ];

        $responsiveOutput->writeKeyValue($summary);

        if ($totalVulnerabilities > 0) {
            $output->writeLine("\n<red>⚠ Security vulnerabilities detected!</red>");
            $output->writeLine("Run <cyan>php console.php security:audit</cyan> for details");
        } else {
            $output->writeLine("\n<green>✓ No security vulnerabilities found!</green>");
        }

        if (!empty($auditData['abandoned'])) {
            $output->writeLine("\n<yellow>Abandoned Packages:</yellow>");
            foreach ($auditData['abandoned'] as $package => $replacement) {
                $replacementText = $replacement ? " (use {$replacement} instead)" : '';
                $output->writeLine("  • {$package}{$replacementText}");
            }
        }

        return $totalVulnerabilities > 0 ? ExitCode::FAILURE : ExitCode::SUCCESS;
    }

    #[ConsoleCommand(
        name: 'security:audit-details',
        description: 'Show detailed information about a specific package vulnerability'
    )]
    public function auditDetails(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $packageName = $input->getArgument('package');

        if (!$packageName) {
            $output->writeLine('<red>Package name is required</red>');
            $output->writeLine('Usage: php console.php security:audit-details <package>');
            return ExitCode::INVALID_ARGUMENTS;
        }

        // Execute composer audit with JSON format
        exec('composer audit --format=json 2>&1', $outputLines);
        $auditData = json_decode(implode("\n", $outputLines), true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            $output->writeLine('<red>Failed to parse audit results</red>');
            return ExitCode::FAILURE;
        }

        // Find package advisories
        $advisories = $auditData['advisories'][$packageName] ?? null;

        if (!$advisories) {
            $output->writeLine("<yellow>No vulnerabilities found for package: {$packageName}</yellow>");
            return ExitCode::SUCCESS;
        }

        $output->writeLine("<yellow>Vulnerabilities for {$packageName}</yellow>\n");

        foreach ($advisories as $advisory) {
            $this->displayAdvisoryDetails($advisory, $output);
            $output->writeLine(str_repeat('─', 80));
        }

        return ExitCode::SUCCESS;
    }

    private function displayJsonResults(array $auditData, ConsoleOutput $output): void
    {
        $advisories = $auditData['advisories'] ?? [];
        $abandoned = $auditData['abandoned'] ?? [];

        if (empty($advisories) && empty($abandoned)) {
            $output->writeLine('<green>✓ No security issues found!</green>');
            return;
        }

        if (!empty($advisories)) {
            $output->writeLine("<red>Security Vulnerabilities:</red>\n");

            foreach ($advisories as $packageName => $packageAdvisories) {
                $output->writeLine("<yellow>{$packageName}</yellow>");

                foreach ($packageAdvisories as $advisory) {
                    $severity = $advisory['severity'] ?? 'unknown';
                    $severityColor = $this->getSeverityColor($severity);
                    $cve = $advisory['cve'] ?? 'N/A';
                    $title = $advisory['title'] ?? 'No title';

                    $output->writeLine("  • [{$severityColor}{$severity}</color>] {$cve}: {$title}");
                    if (!empty($advisory['link'])) {
                        $output->writeLine("    Link: <cyan>{$advisory['link']}</cyan>");
                    }
                }
                $output->writeLine('');
            }
        }

        if (!empty($abandoned)) {
            $output->writeLine("<yellow>Abandoned Packages:</yellow>\n");
            foreach ($abandoned as $package => $replacement) {
                $replacementText = $replacement ? " (use {$replacement})" : '';
                $output->writeLine("  • {$package}{$replacementText}");
            }
        }
    }

    private function displayAdvisoryDetails(array $advisory, ConsoleOutput $output): void
    {
        $responsiveOutput = ResponsiveOutput::create($output);

        $details = [
            'Title' => $advisory['title'] ?? 'Unknown',
            'CVE' => $advisory['cve'] ?? 'N/A',
            'Severity' => $this->formatSeverity($advisory['severity'] ?? 'unknown'),
            'Affected Versions' => $advisory['affectedVersions'] ?? 'N/A',
            'Source' => $advisory['sources'][0]['remoteId'] ?? 'N/A',
        ];

        $responsiveOutput->writeKeyValue($details);

        if (!empty($advisory['link'])) {
            $output->writeLine("\n<cyan>More Info:</cyan> {$advisory['link']}");
        }

        $output->writeLine('');
    }

    private function getSeverityColor(string $severity): string
    {
        return match (strtolower($severity)) {
            'critical' => '<red>',
            'high' => '<yellow>',
            'medium' => '<cyan>',
            'low' => '<gray>',
            default => '<white>',
        };
    }

    private function formatSeverity(string $severity): string
    {
        $color = $this->getSeverityColor($severity);
        return "{$color}" . strtoupper($severity) . '</color>';
    }

    private function determineExitCode(array $auditData, bool $failOnVulnerabilities): ExitCode
    {
        $hasVulnerabilities = !empty($auditData['advisories']);

        if ($hasVulnerabilities && $failOnVulnerabilities) {
            return ExitCode::FAILURE;
        }

        return ExitCode::SUCCESS;
    }
}
