<?php

declare(strict_types=1);

namespace App\Framework\Deployment\Pipeline\Commands;

use App\Framework\Console\Attribute\ConsoleCommand;
use App\Framework\Console\ExitCode;
use App\Framework\Deployment\Pipeline\Services\DeploymentPipelineService;
use App\Framework\Deployment\Pipeline\Stages\AnsibleDeployStage;
use App\Framework\Deployment\Pipeline\Stages\BuildStage;
use App\Framework\Deployment\Pipeline\Stages\DeployStage;
use App\Framework\Deployment\Pipeline\Stages\HealthCheckStage;
use App\Framework\Deployment\Pipeline\Stages\TestStage;
use App\Framework\Deployment\Pipeline\ValueObjects\DeploymentEnvironment;
use App\Framework\Deployment\Pipeline\ValueObjects\PipelineStatus;

/**
 * Deployment Pipeline Console Commands
 *
 * CLI interface for running deployment pipelines.
 */
final readonly class DeploymentPipelineCommands
{
    public function __construct(
        private BuildStage $buildStage,
        private TestStage $testStage,
        private DeployStage $deployStage,
        private AnsibleDeployStage $ansibleDeployStage,
        private HealthCheckStage $healthCheckStage
    ) {}

    #[ConsoleCommand(name: 'deploy:dev', description: 'Deploy to development environment')]
    public function deployDev(): ExitCode
    {
        return $this->runPipeline(DeploymentEnvironment::DEVELOPMENT);
    }

    #[ConsoleCommand(name: 'deploy:staging', description: 'Deploy to staging environment')]
    public function deployStaging(): ExitCode
    {
        return $this->runPipeline(DeploymentEnvironment::STAGING);
    }

    #[ConsoleCommand(name: 'deploy:production', description: 'Deploy to production environment')]
    public function deployProduction(?bool $force = null): ExitCode
    {
        // Skip confirmation if --force flag is provided
        if ($force !== true) {
            echo "⚠️  Production Deployment\n";
            echo "   This will deploy to the production environment.\n";
            echo "   Are you sure? (yes/no): ";

            $confirmation = trim(fgets(STDIN) ?? '');

            if ($confirmation !== 'yes') {
                echo "❌ Production deployment cancelled.\n";
                return ExitCode::FAILURE;
            }
        } else {
            echo "⚠️  Production Deployment (forced)\n";
            echo "\n";
        }

        return $this->runPipeline(DeploymentEnvironment::PRODUCTION);
    }

    private function runPipeline(DeploymentEnvironment $environment): int
    {
        echo "\n";
        echo "🚀 Starting Deployment Pipeline\n";
        echo "   Environment: {$environment->getDisplayName()}\n";
        echo "\n";

        // Create pipeline with appropriate stages
        $stages = $this->getStagesForEnvironment($environment);

        $pipeline = new DeploymentPipelineService(
            stages: $stages,
            eventBus: app()->get(\App\Framework\EventBus\EventBus::class),
            logger: app()->get(\App\Framework\Logging\Logger::class)
        );

        // Execute pipeline
        $result = $pipeline->execute($environment);

        echo "\n";
        echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
        echo "\n";

        // Display result
        echo "{$result->status->getIcon()}  Pipeline Status: {$result->status->value}\n";
        echo "   Environment: {$environment->getDisplayName()}\n";
        echo "   Duration: {$result->totalDuration->toHumanReadable()}\n";
        echo "   Stages Completed: {$result->getCompletedStagesCount()}\n";

        if ($result->isSuccess()) {
            echo "   Successful Stages: {$result->getSuccessfulStagesCount()}/{$result->getCompletedStagesCount()}\n";
        }

        echo "\n";

        // Show stage details
        foreach ($result->stageResults as $stageResult) {
            $icon = $stageResult->isSuccess() ? '✅' : '❌';
            echo "   {$icon} {$stageResult->stage->value}: {$stageResult->duration->toHumanReadable()}\n";

            if ($stageResult->isFailed() && $stageResult->error) {
                echo "      Error: {$stageResult->error}\n";
            }
        }

        echo "\n";

        if ($result->wasRolledBack()) {
            echo "⚠️  Pipeline was rolled back due to failure.\n";
        }

        if ($result->isFailed()) {
            $failedStage = $result->getFailedStage();
            if ($failedStage) {
                echo "❌ Failed at stage: {$failedStage->stage->value}\n";
                if ($failedStage->error) {
                    echo "   Error: {$failedStage->error}\n";
                }
            }
        }

        echo "\n";

        return $result->isSuccess() ? ExitCode::SUCCESS : ExitCode::FAILURE;
    }

    /**
     * @return array<PipelineStageInterface>
     */
    private function getStagesForEnvironment(DeploymentEnvironment $environment): array
    {
        // Development: All stages including tests
        if ($environment === DeploymentEnvironment::DEVELOPMENT) {
            return [
                $this->buildStage,
                $this->testStage,
                $this->deployStage,
                $this->healthCheckStage,
            ];
        }

        // Staging: All stages including tests
        if ($environment === DeploymentEnvironment::STAGING) {
            return [
                $this->buildStage,
                $this->testStage,
                $this->deployStage,
                $this->healthCheckStage,
            ];
        }

        // Production: Skip tests (already tested in staging), use Ansible for deployment
        return [
            $this->buildStage,
            $this->ansibleDeployStage, // Use Ansible for production deployments
            $this->healthCheckStage,
        ];
    }
}
