<?php

declare(strict_types=1);

namespace App\Framework\EventSourcing\Replay;

use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ExitCode;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\EventSourcing\AggregateId;

/**
 * Event Replay Console Commands
 *
 * Manage event replay and time travel debugging via CLI
 */
final readonly class ReplayCommands
{
    public function __construct(
        private EventReplayer $replayer,
        private TimeTravelDebugger $timeTravelDebugger,
        private EventHistoryVisualizer $visualizer,
        private ProjectionRebuilder $projectionRebuilder
    ) {
    }

    /**
     * Replay events for aggregate
     *
     * Usage: php console.php replay:events <aggregate-id> [strategy]
     */
    #[ConsoleCommand('replay:events', 'Replay events for aggregate')]
    public function events(ConsoleInput $input): int
    {
        $aggregateIdStr = $input->getArgument('aggregate-id');
        $strategyType = $input->getArgument('strategy') ?? 'full';

        if (! $aggregateIdStr) {
            echo "Error: Aggregate ID required\n";
            echo "Usage: replay:events <aggregate-id> [strategy]\n";
            echo "Strategies: full, from-version, time-range, event-type\n";

            return ExitCode::ERROR;
        }

        $aggregateId = $this->createAggregateId($aggregateIdStr);
        $strategy = $this->createStrategy($strategyType, $input);

        echo "Replaying events for aggregate: {$aggregateIdStr}\n";
        echo "Strategy: {$strategy->getDescription()}\n\n";

        $count = 0;
        $this->replayer->replayWithCallback(
            aggregateId: $aggregateId,
            strategy: $strategy,
            callback: function ($envelope) use (&$count) {
                $count++;
                echo "[{$count}] Version {$envelope->version}: ";
                echo class_basename(get_class($envelope->event));
                echo " @ {$envelope->timestamp->format('Y-m-d H:i:s')}\n";
            }
        );

        echo "\nTotal events replayed: {$count}\n";

        return ExitCode::SUCCESS;
    }

    /**
     * Time travel to specific point
     *
     * Usage: php console.php replay:time-travel <aggregate-id> <timestamp>
     */
    #[ConsoleCommand('replay:time-travel', 'Time travel to specific point')]
    public function timeTravel(ConsoleInput $input): int
    {
        $aggregateIdStr = $input->getArgument('aggregate-id');
        $timestampStr = $input->getArgument('timestamp');

        if (! $aggregateIdStr || ! $timestampStr) {
            echo "Error: Aggregate ID and timestamp required\n";
            echo "Usage: replay:time-travel <aggregate-id> <timestamp>\n";
            echo "Example: replay:time-travel order-123 '2024-01-15 14:30:00'\n";

            return ExitCode::ERROR;
        }

        $aggregateId = $this->createAggregateId($aggregateIdStr);
        $targetTime = Timestamp::fromString($timestampStr);

        echo "Time traveling to: {$targetTime->format('Y-m-d H:i:s')}\n\n";

        // Note: Would need aggregate factory
        echo "State reconstruction would happen here\n";
        echo "(Requires aggregate factory implementation)\n";

        return ExitCode::SUCCESS;
    }

    /**
     * Show event history timeline
     *
     * Usage: php console.php replay:timeline <aggregate-id>
     */
    #[ConsoleCommand('replay:timeline', 'Show event history timeline')]
    public function timeline(ConsoleInput $input): int
    {
        $aggregateIdStr = $input->getArgument('aggregate-id');

        if (! $aggregateIdStr) {
            echo "Error: Aggregate ID required\n";

            return ExitCode::ERROR;
        }

        $aggregateId = $this->createAggregateId($aggregateIdStr);

        $result = $this->visualizer->generateTimeline($aggregateId);

        echo "Event Timeline for: {$aggregateIdStr}\n";
        echo str_repeat('=', 80) . "\n\n";

        // Statistics
        $stats = $result['statistics'];
        echo "Statistics:\n";
        echo "  Total Events: {$stats['total_events']}\n";
        echo "  Time Span: {$stats['time_span_hours']} hours\n";
        echo "  Avg Events/Day: {$stats['average_events_per_day']}\n";
        echo "  Unique Event Types: {$stats['unique_event_types']}\n\n";

        // Timeline
        echo "Timeline:\n";
        echo str_repeat('-', 80) . "\n";

        foreach ($result['timeline'] as $entry) {
            printf(
                "v%-5d %s  %s\n",
                $entry['version'],
                $entry['timestamp'],
                $entry['event_type']
            );
        }

        return ExitCode::SUCCESS;
    }

    /**
     * Show event statistics
     *
     * Usage: php console.php replay:stats <aggregate-id>
     */
    #[ConsoleCommand('replay:stats', 'Show event statistics')]
    public function stats(ConsoleInput $input): int
    {
        $aggregateIdStr = $input->getArgument('aggregate-id');

        if (! $aggregateIdStr) {
            echo "Error: Aggregate ID required\n";

            return ExitCode::ERROR;
        }

        $aggregateId = $this->createAggregateId($aggregateIdStr);

        $summary = $this->visualizer->getStreamSummary($aggregateId);

        echo "Event Stream Summary\n";
        echo str_repeat('=', 80) . "\n\n";

        echo "Aggregate ID: {$summary['aggregate_id']}\n";
        echo "Total Events: {$summary['total_events']}\n\n";

        echo "First Event:\n";
        echo "  Version: {$summary['first_event']['version']}\n";
        echo "  Type: {$summary['first_event']['type']}\n";
        echo "  Timestamp: {$summary['first_event']['timestamp']}\n\n";

        echo "Last Event:\n";
        echo "  Version: {$summary['last_event']['version']}\n";
        echo "  Type: {$summary['last_event']['type']}\n";
        echo "  Timestamp: {$summary['last_event']['timestamp']}\n\n";

        echo "Time Span:\n";
        echo "  Duration: {$summary['time_span']['duration_hours']} hours\n";
        echo "  Avg Events/Day: {$summary['average_events_per_day']}\n\n";

        echo "Event Type Distribution:\n";
        foreach ($summary['event_types'] as $type => $count) {
            $percentage = round(($count / $summary['total_events']) * 100, 1);
            printf("  %-30s %5d (%5.1f%%)\n", $type, $count, $percentage);
        }

        return ExitCode::SUCCESS;
    }

    /**
     * Rebuild projection
     *
     * Usage: php console.php replay:rebuild-projection <projection-name>
     */
    #[ConsoleCommand('replay:rebuild-projection', 'Rebuild projection')]
    public function rebuildProjection(ConsoleInput $input): int
    {
        $projectionName = $input->getArgument('projection-name');

        if (! $projectionName) {
            echo "Error: Projection name required\n";
            echo "Usage: replay:rebuild-projection <projection-name>\n";

            return ExitCode::ERROR;
        }

        echo "Rebuilding projection: {$projectionName}...\n";

        try {
            $result = $this->projectionRebuilder->rebuildProjection($projectionName);

            echo "✓ Projection rebuilt successfully\n";
            echo "Strategy: {$result['strategy']}\n";
            echo "Status: {$result['status']}\n";
            echo "Last Version: {$result['last_version']}\n";

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

            return ExitCode::ERROR;
        }
    }

    /**
     * Rebuild all projections
     *
     * Usage: php console.php replay:rebuild-all
     */
    #[ConsoleCommand('replay:rebuild-all', 'Rebuild all projections')]
    public function rebuildAll(ConsoleInput $input): int
    {
        echo "Rebuilding all projections...\n";

        try {
            $results = $this->projectionRebuilder->rebuildAllProjections();

            echo "✓ All projections rebuilt successfully\n";
            echo "Total: " . count($results) . " projection(s)\n";

            foreach ($results as $result) {
                echo "  - {$result->projectionName}: {$result->status->value}\n";
            }

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

            return ExitCode::ERROR;
        }
    }

    private function createAggregateId(string $id): AggregateId
    {
        return new class ($id) implements AggregateId {
            public function __construct(private readonly string $id)
            {
            }

            public function toString(): string
            {
                return $this->id;
            }
        };
    }

    private function createStrategy(string $type, ConsoleInput $input): ReplayStrategy
    {
        return match ($type) {
            'full' => new FullReplayStrategy(),
            'from-version' => new FromVersionReplayStrategy(
                (int) ($input->getArgument('version') ?? 0)
            ),
            'time-range' => TimeRangeReplayStrategy::lastDays(
                (int) ($input->getArgument('days') ?? 7)
            ),
            'today' => TimeRangeReplayStrategy::today(),
            'yesterday' => TimeRangeReplayStrategy::yesterday(),
            default => new FullReplayStrategy()
        };
    }
}
