<?php

declare(strict_types=1);

namespace App\Framework\Queue\Services;

use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Queue\Exceptions\AllStepsCompletedException;

/**
 * Helper class for tracking progress through multiple steps
 */
final class StepProgressTracker
{
    private int $currentStepIndex = 0;

    private array $completedSteps = [];

    public function __construct(
        private readonly string $jobId,
        private readonly array $steps,
        private readonly ProgressManager $progressManager
    ) {
        if (empty($steps)) {
            throw new \InvalidArgumentException('Steps array cannot be empty');
        }

        // Validate step structure
        foreach ($steps as $index => $step) {
            if (! is_array($step) || ! isset($step['name'], $step['description'])) {
                throw new \InvalidArgumentException("Step at index {$index} must have 'name' and 'description' keys");
            }
        }
    }

    public function start(string $message = 'Starting multi-step job'): void
    {
        $this->progressManager->startJob($this->jobId, $message);
    }

    public function completeCurrentStep(?array $metadata = null): void
    {
        if ($this->currentStepIndex >= count($this->steps)) {
            throw AllStepsCompletedException::forJob($this->jobId, count($this->steps));
        }

        $step = $this->steps[$this->currentStepIndex];

        $this->progressManager->completeJobStep(
            $this->jobId,
            $step['name'],
            $step['description'],
            $metadata
        );

        $this->completedSteps[] = $step['name'];
        $this->currentStepIndex++;

        // Update overall progress percentage
        $percentage = ($this->currentStepIndex / count($this->steps)) * 100;
        $message = sprintf(
            'Completed step %d of %d: %s',
            $this->currentStepIndex,
            count($this->steps),
            $step['description']
        );

        $this->progressManager->updateJobProgress(
            $this->jobId,
            $percentage,
            $message,
            [
                'current_step' => $this->currentStepIndex,
                'total_steps' => count($this->steps),
                'completed_steps' => $this->completedSteps,
            ]
        );
    }

    public function updateCurrentStepProgress(
        float $stepPercentage,
        string $message,
        ?array $metadata = null
    ): void {
        if ($this->currentStepIndex >= count($this->steps)) {
            throw AllStepsCompletedException::forJob($this->jobId, count($this->steps));
        }

        // Calculate overall percentage: completed steps + current step progress
        $completedStepsPercentage = ($this->currentStepIndex / count($this->steps)) * 100;
        $currentStepContribution = ($stepPercentage / 100) * (100 / count($this->steps));
        $totalPercentage = $completedStepsPercentage + $currentStepContribution;

        $step = $this->steps[$this->currentStepIndex];
        $fullMessage = sprintf(
            'Step %d of %d (%s): %s',
            $this->currentStepIndex + 1,
            count($this->steps),
            $step['name'],
            $message
        );

        $fullMetadata = array_merge($metadata ?? [], [
            'current_step' => $this->currentStepIndex + 1,
            'total_steps' => count($this->steps),
            'step_name' => $step['name'],
            'step_percentage' => $stepPercentage,
            'completed_steps' => $this->completedSteps,
        ]);

        $this->progressManager->updateJobProgress(
            $this->jobId,
            $totalPercentage,
            $fullMessage,
            $fullMetadata
        );
    }

    public function complete(string $message = 'All steps completed successfully'): void
    {
        // Complete any remaining steps if not all were explicitly completed
        while ($this->currentStepIndex < count($this->steps)) {
            $this->completeCurrentStep();
        }

        $this->progressManager->completeJob($this->jobId, $message);
    }

    public function fail(string $message, ?\Throwable $exception = null): void
    {
        $step = $this->getCurrentStep();
        $fullMessage = sprintf(
            'Failed on step %d of %d (%s): %s',
            $this->currentStepIndex + 1,
            count($this->steps),
            $step['name'] ?? 'unknown',
            $message
        );

        $this->progressManager->failJob($this->jobId, $fullMessage, $exception);
    }

    public function getCurrentStep(): ?array
    {
        if ($this->currentStepIndex >= count($this->steps)) {
            return null;
        }

        return $this->steps[$this->currentStepIndex];
    }

    public function getRemainingSteps(): array
    {
        return array_slice($this->steps, $this->currentStepIndex);
    }

    public function getCompletedSteps(): array
    {
        return $this->completedSteps;
    }

    public function isComplete(): bool
    {
        return $this->currentStepIndex >= count($this->steps);
    }

    public function getProgress(): float
    {
        return ($this->currentStepIndex / count($this->steps)) * 100;
    }
}
