<?php

declare(strict_types=1);

namespace App\Framework\Deployment\Pipeline\ValueObjects;

use App\Framework\Core\ValueObjects\Duration;

/**
 * Value Object representing a pipeline execution history entry
 */
final readonly class PipelineHistoryEntry
{
    /**
     * @param array<array{stage: string, status: string, duration_ms: int, error: ?string}> $stagesData
     */
    public function __construct(
        public PipelineId $pipelineId,
        public DeploymentEnvironment $environment,
        public PipelineStatus $status,
        public array $stagesData,
        public Duration $totalDuration,
        public ?\DateTimeImmutable $startedAt,
        public ?\DateTimeImmutable $completedAt,
        public ?string $error = null,
        public bool $wasRolledBack = false,
        public ?PipelineStage $failedStage = null
    ) {}

    /**
     * Create from PipelineResult
     */
    public static function fromPipelineResult(
        PipelineResult $result,
        \DateTimeImmutable $startedAt,
        \DateTimeImmutable $completedAt
    ): self {
        $stagesData = array_map(
            fn($stageResult) => [
                'stage' => $stageResult->stage->value,
                'status' => $stageResult->success ? 'success' : 'failed',
                'duration_ms' => (int) $stageResult->duration->toMilliseconds(),
                'error' => $stageResult->error,
                'output' => $stageResult->output
            ],
            $result->stageResults
        );

        return new self(
            pipelineId: $result->pipelineId,
            environment: $result->environment,
            status: $result->status,
            stagesData: $stagesData,
            totalDuration: $result->totalDuration,
            startedAt: $startedAt,
            completedAt: $completedAt,
            error: $result->error,
            wasRolledBack: $result->wasRolledBack(),
            failedStage: $result->getFailedStage()?->stage
        );
    }

    /**
     * Convert to array for database storage
     */
    public function toArray(): array
    {
        return [
            'pipeline_id' => $this->pipelineId->value,
            'environment' => $this->environment->value,
            'status' => $this->status->value,
            'stages_data' => json_encode($this->stagesData),
            'total_duration_ms' => (int) $this->totalDuration->toMilliseconds(),
            'error' => $this->error,
            'was_rolled_back' => $this->wasRolledBack,
            'failed_stage' => $this->failedStage?->value,
            'started_at' => $this->startedAt?->format('Y-m-d H:i:s'),
            'completed_at' => $this->completedAt?->format('Y-m-d H:i:s')
        ];
    }

    /**
     * Create from database row
     */
    public static function fromArray(array $data): self
    {
        return new self(
            pipelineId: PipelineId::fromString($data['pipeline_id']),
            environment: DeploymentEnvironment::from($data['environment']),
            status: PipelineStatus::from($data['status']),
            stagesData: json_decode($data['stages_data'], true),
            totalDuration: Duration::fromMilliseconds((int) $data['total_duration_ms']),
            startedAt: new \DateTimeImmutable($data['started_at']),
            completedAt: new \DateTimeImmutable($data['completed_at']),
            error: $data['error'] ?? null,
            wasRolledBack: (bool) $data['was_rolled_back'],
            failedStage: $data['failed_stage'] ? PipelineStage::from($data['failed_stage']) : null
        );
    }

    /**
     * Get success rate (percentage of successful stages)
     */
    public function getSuccessRate(): float
    {
        if (empty($this->stagesData)) {
            return 0.0;
        }

        $successCount = count(array_filter(
            $this->stagesData,
            fn($stage) => $stage['status'] === 'success'
        ));

        return ($successCount / count($this->stagesData)) * 100;
    }

    /**
     * Get stage count
     */
    public function getStageCount(): int
    {
        return count($this->stagesData);
    }

    /**
     * Check if execution was successful
     */
    public function isSuccess(): bool
    {
        return $this->status === PipelineStatus::SUCCESS;
    }
}
