<?php

declare(strict_types=1);

namespace App\Framework\Deployment\Pipeline\Stages;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Deployment\Pipeline\ValueObjects\DeploymentEnvironment;
use App\Framework\Deployment\Pipeline\ValueObjects\PipelineStage;
use App\Framework\Deployment\Pipeline\ValueObjects\StageResult;
use App\Framework\Logging\Logger;
use App\Framework\Process\Process;
use App\Framework\Process\ValueObjects\Command;

/**
 * Test Stage
 *
 * Runs automated tests (Pest).
 */
final readonly class TestStage implements PipelineStageInterface
{
    public function __construct(
        private Process $process,
        private Logger $logger
    ) {}

    public function getStage(): PipelineStage
    {
        return PipelineStage::TEST;
    }

    public function execute(DeploymentEnvironment $environment): StageResult
    {
        if ($this->shouldSkip($environment)) {
            return StageResult::success(
                stage: $this->getStage(),
                duration: Duration::fromMilliseconds(0),
                output: 'Tests skipped for production environment'
            );
        }

        $startTime = microtime(true);

        $this->logger->info('Running tests', [
            'environment' => $environment->value
        ]);

        try {
            $result = $this->process->run(
                Command::fromString('./vendor/bin/pest --stop-on-failure'),
                timeout: Duration::fromMinutes(5)
            );

            $duration = Duration::fromSeconds(microtime(true) - $startTime);

            if (!$result->isSuccess()) {
                return StageResult::failure(
                    stage: $this->getStage(),
                    duration: $duration,
                    error: 'Tests failed',
                    output: $result->stderr
                );
            }

            return StageResult::success(
                stage: $this->getStage(),
                duration: $duration,
                output: $result->stdout
            );

        } catch (\Throwable $e) {
            $duration = Duration::fromSeconds(microtime(true) - $startTime);

            $this->logger->error('Test execution failed', [
                'error' => $e->getMessage()
            ]);

            return StageResult::failure(
                stage: $this->getStage(),
                duration: $duration,
                error: $e->getMessage()
            );
        }
    }

    public function canRollback(): bool
    {
        return false;
    }

    public function rollback(DeploymentEnvironment $environment): StageResult
    {
        throw new \LogicException('Test stage does not support rollback');
    }

    public function getDescription(): string
    {
        return $this->getStage()->getDescription();
    }

    public function shouldSkip(DeploymentEnvironment $environment): bool
    {
        // Skip tests in production (already tested in staging)
        return $environment === DeploymentEnvironment::PRODUCTION;
    }
}
