<?php

declare(strict_types=1);

namespace App\Framework\ErrorBoundaries\Commands;

use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ExitCode;
use App\Framework\ErrorBoundaries\BoundaryConfig;
use App\Framework\ErrorBoundaries\ErrorBoundaryFactory;

/**
 * Console commands for error boundary management
 */
final readonly class BoundaryCommand
{
    public function __construct(
        private ErrorBoundaryFactory $boundaryFactory
    ) {
    }

    #[ConsoleCommand(
        name: 'boundary:test',
        description: 'Test error boundary functionality'
    )]
    public function test(ConsoleInput $input, ConsoleOutput $output): int
    {
        $args = $input->getArguments();
        $testType = $args[0] ?? 'basic';

        $output->writeLine('<info>Testing Error Boundary functionality...</info>');
        $output->writeLine('');

        return match ($testType) {
            'basic' => $this->testBasicFunctionality($output),
            'retry' => $this->testRetryStrategies($output),
            'circuit' => $this->testCircuitBreaker($output),
            'bulk' => $this->testBulkOperations($output),
            'timeout' => $this->testTimeout($output),
            default => $this->showTestOptions($output),
        };
    }

    #[ConsoleCommand(
        name: 'boundary:stats',
        description: 'Show error boundary statistics'
    )]
    public function stats(ConsoleInput $input, ConsoleOutput $output): int
    {
        $output->writeLine('<info>Error Boundary Statistics</info>');
        $output->writeLine('');

        // Get circuit breaker states
        $states = $this->getCircuitBreakerStates();

        if (empty($states)) {
            $output->writeLine('<comment>No active circuit breakers found.</comment>');
        } else {
            $output->writeLine('<comment>Active Circuit Breakers:</comment>');
            foreach ($states as $boundary => $state) {
                $statusIcon = $state['failures'] >= 5 ? '🔴' : '🟢';
                $output->writeLine("  {$statusIcon} <comment>{$boundary}</comment>: {$state['failures']} failures");

                if ($state['failures'] > 0) {
                    $lastFailure = date('Y-m-d H:i:s', $state['opened_at']);
                    $output->writeLine("    Last failure: {$lastFailure}");
                }
            }
        }

        return ExitCode::SUCCESS;
    }

    #[ConsoleCommand(
        name: 'boundary:reset',
        description: 'Reset circuit breaker states'
    )]
    public function reset(ConsoleInput $input, ConsoleOutput $output): int
    {
        $args = $input->getArguments();
        $boundaryName = $args[0] ?? null;

        if ($boundaryName) {
            $this->resetCircuitBreaker($boundaryName);
            $output->writeLine("<info>Reset circuit breaker for boundary: {$boundaryName}</info>");
        } else {
            $this->resetAllCircuitBreakers();
            $output->writeLine('<info>Reset all circuit breakers</info>');
        }

        return ExitCode::SUCCESS;
    }

    private function testBasicFunctionality(ConsoleOutput $output): int
    {
        $output->writeLine('<comment>Testing basic error boundary functionality...</comment>');

        $boundary = $this->boundaryFactory->create('test_basic', BoundaryConfig::development());

        // Test successful operation
        $result = $boundary->execute(
            operation: fn () => 'success',
            fallback: fn () => 'fallback'
        );

        $successIcon = $result === 'success' ? '✅' : '❌';
        $output->writeLine("  {$successIcon} Success case: {$result}");

        // Test fallback
        $result = $boundary->execute(
            operation: fn () => throw new \Exception('Test failure'),
            fallback: fn () => 'fallback_executed'
        );

        $fallbackIcon = $result === 'fallback_executed' ? '✅' : '❌';
        $output->writeLine("  {$fallbackIcon} Fallback case: {$result}");

        return ExitCode::SUCCESS;
    }

    private function testRetryStrategies(ConsoleOutput $output): int
    {
        $output->writeLine('<comment>Testing retry strategies...</comment>');

        $strategies = [
            'FIXED' => BoundaryConfig::development(),
            'EXPONENTIAL' => new BoundaryConfig(maxRetries: 3, retryStrategy: \App\Framework\ErrorBoundaries\RetryStrategy::EXPONENTIAL),
            'EXPONENTIAL_JITTER' => new BoundaryConfig(maxRetries: 3, retryStrategy: \App\Framework\ErrorBoundaries\RetryStrategy::EXPONENTIAL_JITTER),
        ];

        foreach ($strategies as $name => $config) {
            $boundary = $this->boundaryFactory->create("test_retry_{$name}", $config);

            $attemptCount = 0;
            $result = $boundary->executeOptional(function () use (&$attemptCount) {
                $attemptCount++;
                if ($attemptCount < 3) {
                    throw new \Exception("Attempt {$attemptCount}");
                }

                return "Success after {$attemptCount} attempts";
            });

            $icon = $result !== null ? '✅' : '❌';
            $output->writeLine("  {$icon} {$name}: {$result}");
        }

        return ExitCode::SUCCESS;
    }

    private function testCircuitBreaker(ConsoleOutput $output): int
    {
        $output->writeLine('<comment>Testing circuit breaker...</comment>');

        $config = new BoundaryConfig(
            circuitBreakerEnabled: true,
            circuitBreakerThreshold: 2,
            maxRetries: 1
        );

        $boundary = $this->boundaryFactory->create('test_circuit', $config);

        // Trigger failures to open circuit
        for ($i = 1; $i <= 3; $i++) {
            $result = $boundary->executeWithCircuitBreaker(
                operation: fn () => throw new \Exception("Failure {$i}"),
                fallback: fn () => "Fallback {$i}"
            );

            $output->writeLine("  Attempt {$i}: {$result}");
        }

        $output->writeLine('  <info>Circuit should now be open, subsequent calls should use fallback immediately</info>');

        return ExitCode::SUCCESS;
    }

    private function testBulkOperations(ConsoleOutput $output): int
    {
        $output->writeLine('<comment>Testing bulk operations...</comment>');

        $boundary = $this->boundaryFactory->create('test_bulk', BoundaryConfig::development());

        $items = range(1, 10);
        $result = $boundary->executeBulk($items, function ($item) {
            if ($item % 3 === 0) {
                throw new \Exception("Item {$item} failed");
            }

            return $item * 2;
        });

        $output->writeLine("  Processed: {$result->getProcessedCount()}/{$result->getTotalCount()}");
        $output->writeLine("  Successful: {$result->getSuccessCount()}");
        $output->writeLine("  Failed: {$result->getErrorCount()}");
        $output->writeLine("  Success rate: " . round($result->getSuccessRate(), 1) . "%");

        return ExitCode::SUCCESS;
    }

    private function testTimeout(ConsoleOutput $output): int
    {
        $output->writeLine('<comment>Testing timeout functionality...</comment>');

        $boundary = $this->boundaryFactory->create('test_timeout', BoundaryConfig::development());

        // Test normal operation
        $result = $boundary->executeWithTimeout(
            operation: fn () => 'quick_operation',
            fallback: fn () => 'timeout_fallback',
            timeoutSeconds: 1
        );

        $quickIcon = $result === 'quick_operation' ? '✅' : '❌';
        $output->writeLine("  {$quickIcon} Quick operation: {$result}");

        // Note: In real PHP, we can't actually test timeouts without async operations
        $output->writeLine('  <comment>Note: True timeout testing requires async operations</comment>');

        return ExitCode::SUCCESS;
    }

    private function showTestOptions(ConsoleOutput $output): int
    {
        $output->writeLine('<error>Invalid test type. Available options:</error>');
        $output->writeLine('  <comment>basic</comment>   - Test basic success/fallback functionality');
        $output->writeLine('  <comment>retry</comment>   - Test retry strategies');
        $output->writeLine('  <comment>circuit</comment> - Test circuit breaker');
        $output->writeLine('  <comment>bulk</comment>    - Test bulk operations');
        $output->writeLine('  <comment>timeout</comment> - Test timeout handling');

        return ExitCode::FAILURE;
    }

    private function getCircuitBreakerStates(): array
    {
        $states = [];
        $tempDir = sys_get_temp_dir();

        $files = glob($tempDir . '/boundary_*_state');

        foreach ($files as $file) {
            $boundaryName = str_replace(
                [$tempDir . '/boundary_', '_state'],
                '',
                $file
            );

            $content = file_get_contents($file);
            $state = json_decode($content, true);

            if ($state) {
                $states[$boundaryName] = $state;
            }
        }

        return $states;
    }

    private function resetCircuitBreaker(string $boundaryName): void
    {
        $stateFile = sys_get_temp_dir() . "/boundary_{$boundaryName}_state";

        if (file_exists($stateFile)) {
            unlink($stateFile);
        }
    }

    private function resetAllCircuitBreakers(): void
    {
        $files = glob(sys_get_temp_dir() . '/boundary_*_state');

        foreach ($files as $file) {
            unlink($file);
        }
    }
}
