<?php

declare(strict_types=1);

namespace App\Framework\EventSourcing\Replay;

use App\Framework\EventSourcing\AggregateId;
use App\Framework\EventSourcing\EventStore;
use App\Framework\EventSourcing\Projections\ProjectionInterface;
use App\Framework\EventSourcing\Projections\ProjectionManager;

/**
 * Projection Rebuilder
 *
 * Rebuilds projections from event history
 */
final readonly class ProjectionRebuilder
{
    public function __construct(
        private EventStore $eventStore,
        private EventReplayer $replayer,
        private ProjectionManager $projectionManager
    ) {
    }

    /**
     * Rebuild projection from scratch
     */
    public function rebuildProjection(
        string $projectionName,
        ?ReplayStrategy $strategy = null
    ): array {
        $strategy = $strategy ?? new FullReplayStrategy();

        // Reset projection first
        $this->projectionManager->rebuildProjection($projectionName);

        // Get all aggregate IDs (would need method on EventStore)
        // For now, rebuild is handled by ProjectionManager
        $state = $this->projectionManager->runProjection($projectionName);

        return [
            'projection' => $projectionName,
            'strategy' => $strategy->getDescription(),
            'status' => $state->status->value,
            'last_version' => $state->lastEventVersion,
        ];
    }

    /**
     * Rebuild projection for specific aggregate
     */
    public function rebuildProjectionForAggregate(
        ProjectionInterface $projection,
        AggregateId $aggregateId,
        ?ReplayStrategy $strategy = null
    ): int {
        $strategy = $strategy ?? new FullReplayStrategy();

        // Reset projection
        $projection->reset();

        // Replay events through projection
        return $this->replayer->replayWithCallback(
            aggregateId: $aggregateId,
            strategy: $strategy,
            callback: fn ($envelope) => $projection->project($envelope)
        );
    }

    /**
     * Rebuild multiple projections
     *
     * @param array<string> $projectionNames
     */
    public function rebuildMultipleProjections(
        array $projectionNames,
        ?ReplayStrategy $strategy = null
    ): array {
        $results = [];

        foreach ($projectionNames as $projectionName) {
            $results[$projectionName] = $this->rebuildProjection($projectionName, $strategy);
        }

        return $results;
    }

    /**
     * Rebuild all projections
     */
    public function rebuildAllProjections(
        ?ReplayStrategy $strategy = null
    ): array {
        // Run all projections (which rebuilds them)
        return $this->projectionManager->runAllProjections();
    }
}
