<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Sourcing\EventStore;

final class EventStoreRuntime
{
    private static ?self $instance = null;

    private EventStream $stream;
    private EventStoreStore $store;

    private function __construct(private readonly string $basePath)
    {
        $this->stream = new EventStream();
        $this->store = new EventStoreStore($basePath);
        $this->hydrate();
    }

    public static function instance(?string $basePath = null): self
    {
        if (self::$instance === null) {
            $basePath ??= self::detectBasePath();
            self::$instance = new self($basePath);
        }
        return self::$instance;
    }

    public static function resetInstance(?string $basePath = null): self
    {
        $basePath ??= self::detectBasePath();
        self::$instance = new self($basePath);
        return self::$instance;
    }

    public function append(string $aggregateId, string $aggregateType, string $eventType, array $payload = [], array $metadata = []): StoredEvent
    {
        $event = $this->stream->append($aggregateId, $aggregateType, $eventType, $payload, $metadata);
        $this->save();
        return $event;
    }

    public function eventsForAggregate(string $aggregateId): array
    {
        return $this->stream->byAggregate($aggregateId);
    }

    public function eventsForType(string $aggregateType): array
    {
        return $this->stream->byType($aggregateType);
    }

    public function hydrate(): void
    {
        $snapshot = $this->store->load();
        if ($snapshot->events() !== []) {
            $this->stream = EventStream::fromArray($snapshot->events());
        }
    }

    public function snapshot(): EventStoreSnapshot
    {
        $events = $this->stream->toArray();
        return new EventStoreSnapshot($events, EventStoreStatistics::from($events), date('c'));
    }

    public function save(): EventStoreSnapshot
    {
        return $this->store->save($this->snapshot());
    }

    public function clear(): void
    {
        $this->stream = new EventStream();
        $this->store->reset();
    }

    public function store(): EventStoreStore { return $this->store; }

    public function summary(): array
    {
        $snapshot = $this->snapshot();

        return [
            'events' => $snapshot->events(),
            'statistics' => $snapshot->statistics(),
            'persisted' => $this->store->exists(),
            'stateFile' => $this->store->stateFile(),
        ];
    }

    private static function detectBasePath(): string
    {
        $candidate = dirname(__DIR__, 5);
        $real = realpath($candidate);
        return $real !== false ? $real : $candidate;
    }
}
