<?php

declare(strict_types=1);

namespace App\Framework\EventSourcing\Sagas;

use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ExitCode;

/**
 * Saga Console Commands
 *
 * Manage sagas via CLI
 */
final readonly class SagaCommands
{
    public function __construct(
        private SagaManager $sagaManager,
        private SagaRepository $repository
    ) {
    }

    /**
     * Start a new saga
     *
     * Usage: php console.php saga:start <saga-name> [data-json]
     */
    #[ConsoleCommand('saga:start', 'Start a new saga')]
    public function start(ConsoleInput $input): int
    {
        $sagaName = $input->getArgument('saga-name');
        $dataJson = $input->getArgument('data-json');

        if (! $sagaName) {
            echo "Error: Saga name required\n";
            echo "Usage: saga:start <saga-name> [data-json]\n";

            return ExitCode::ERROR;
        }

        $initialData = [];
        if ($dataJson) {
            $initialData = json_decode($dataJson, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                echo "Error: Invalid JSON data\n";

                return ExitCode::ERROR;
            }
        }

        try {
            $sagaId = $this->sagaManager->startSaga($sagaName, $initialData);

            echo "✓ Saga started successfully\n";
            echo "Saga ID: {$sagaId->toString()}\n";
            echo "Saga Name: {$sagaName}\n";

            return ExitCode::SUCCESS;
        } catch (\Exception $e) {
            echo "Error starting saga: {$e->getMessage()}\n";

            return ExitCode::ERROR;
        }
    }

    /**
     * Show saga status
     *
     * Usage: php console.php saga:status <saga-id>
     */
    #[ConsoleCommand('saga:status', 'Show saga status')]
    public function status(ConsoleInput $input): int
    {
        $sagaIdStr = $input->getArgument('saga-id');

        if (! $sagaIdStr) {
            echo "Error: Saga ID required\n";

            return ExitCode::ERROR;
        }

        $sagaId = SagaId::fromString($sagaIdStr);
        $state = $this->sagaManager->getSagaState($sagaId);

        if (! $state) {
            echo "Saga not found: {$sagaIdStr}\n";

            return ExitCode::ERROR;
        }

        echo "Saga Status:\n";
        echo str_repeat('=', 60) . "\n";
        echo "Saga ID: {$state->sagaId->toString()}\n";
        echo "Saga Name: {$state->sagaName}\n";
        echo "Status: {$state->status->value}\n";
        echo "Progress: {$state->getProgress()}% ({$state->completedSteps}/{$state->stepCount} steps)\n";
        echo "Started: {$state->startedAt->format('Y-m-d H:i:s')}\n";

        if ($state->completedAt) {
            echo "Completed: {$state->completedAt->format('Y-m-d H:i:s')}\n";
        }

        if ($state->errorMessage) {
            echo "Error: {$state->errorMessage}\n";
        }

        echo "\nSaga Data:\n";
        echo json_encode($state->data, JSON_PRETTY_PRINT) . "\n";

        return ExitCode::SUCCESS;
    }

    /**
     * List all sagas
     *
     * Usage: php console.php saga:list [status]
     */
    #[ConsoleCommand('saga:list', 'List all sagas')]
    public function list(ConsoleInput $input): int
    {
        $statusFilter = $input->getArgument('status');

        $sagas = $statusFilter
            ? $this->repository->findByStatus(SagaStatus::from($statusFilter))
            : $this->repository->findAll();

        if (empty($sagas)) {
            echo "No sagas found\n";

            return ExitCode::SUCCESS;
        }

        echo "Sagas:\n";
        echo str_repeat('=', 80) . "\n";
        printf("%-36s %-25s %-12s %-10s\n", 'ID', 'Name', 'Status', 'Progress');
        echo str_repeat('-', 80) . "\n";

        foreach ($sagas as $saga) {
            $state = $saga->getState();
            printf(
                "%-36s %-25s %-12s %6.1f%%\n",
                $state->sagaId->toString(),
                $state->sagaName,
                $state->status->value,
                $state->getProgress()
            );
        }

        echo "\nTotal: " . count($sagas) . " saga(s)\n";

        return ExitCode::SUCCESS;
    }

    /**
     * Show running sagas
     *
     * Usage: php console.php saga:running
     */
    #[ConsoleCommand('saga:running', 'Show running sagas')]
    public function running(ConsoleInput $input): int
    {
        $runningSagas = $this->sagaManager->getRunningSagas();

        if (empty($runningSagas)) {
            echo "No running sagas\n";

            return ExitCode::SUCCESS;
        }

        echo "Running Sagas:\n";
        echo str_repeat('=', 80) . "\n";

        foreach ($runningSagas as $saga) {
            $state = $saga->getState();

            echo "ID: {$state->sagaId->toString()}\n";
            echo "Name: {$state->sagaName}\n";
            echo "Progress: {$state->getProgress()}% ({$state->completedSteps}/{$state->stepCount} steps)\n";
            echo "Started: {$state->startedAt->format('Y-m-d H:i:s')}\n";
            echo str_repeat('-', 80) . "\n";
        }

        echo "\nTotal: " . count($runningSagas) . " running saga(s)\n";

        return ExitCode::SUCCESS;
    }

    /**
     * Complete saga manually
     *
     * Usage: php console.php saga:complete <saga-id>
     */
    #[ConsoleCommand('saga:complete', 'Complete saga manually')]
    public function complete(ConsoleInput $input): int
    {
        $sagaIdStr = $input->getArgument('saga-id');

        if (! $sagaIdStr) {
            echo "Error: Saga ID required\n";

            return ExitCode::ERROR;
        }

        try {
            $sagaId = SagaId::fromString($sagaIdStr);
            $this->sagaManager->completeSaga($sagaId);

            echo "✓ Saga completed successfully\n";

            return ExitCode::SUCCESS;
        } catch (\Exception $e) {
            echo "Error completing saga: {$e->getMessage()}\n";

            return ExitCode::ERROR;
        }
    }

    /**
     * Compensate (rollback) saga
     *
     * Usage: php console.php saga:compensate <saga-id> <reason>
     */
    #[ConsoleCommand('saga:compensate', 'Compensate (rollback) saga')]
    public function compensate(ConsoleInput $input): int
    {
        $sagaIdStr = $input->getArgument('saga-id');
        $reason = $input->getArgument('reason') ?? 'Manual compensation';

        if (! $sagaIdStr) {
            echo "Error: Saga ID required\n";

            return ExitCode::ERROR;
        }

        try {
            $sagaId = SagaId::fromString($sagaIdStr);
            $saga = $this->sagaManager->getSaga($sagaId);

            if (! $saga) {
                echo "Saga not found: {$sagaIdStr}\n";

                return ExitCode::ERROR;
            }

            echo "Compensating saga: {$saga->getName()}...\n";

            $this->sagaManager->compensateSaga($saga, $reason);

            echo "✓ Saga compensated successfully\n";

            return ExitCode::SUCCESS;
        } catch (\Exception $e) {
            echo "Error compensating saga: {$e->getMessage()}\n";

            return ExitCode::ERROR;
        }
    }

    /**
     * Delete saga
     *
     * Usage: php console.php saga:delete <saga-id>
     */
    #[ConsoleCommand('saga:delete', 'Delete saga')]
    public function delete(ConsoleInput $input): int
    {
        $sagaIdStr = $input->getArgument('saga-id');

        if (! $sagaIdStr) {
            echo "Error: Saga ID required\n";

            return ExitCode::ERROR;
        }

        try {
            $sagaId = SagaId::fromString($sagaIdStr);

            echo "Deleting saga: {$sagaIdStr}...\n";

            $this->repository->delete($sagaId);

            echo "✓ Saga deleted successfully\n";

            return ExitCode::SUCCESS;
        } catch (\Exception $e) {
            echo "Error deleting saga: {$e->getMessage()}\n";

            return ExitCode::ERROR;
        }
    }
}
