<?php

declare(strict_types=1);

namespace App\Framework\EventSourcing;

final readonly class AggregateRoot
{
    public DefaultEventRecorder $eventRecorder;

    public function __construct(public object $state, public AggregateId $id, int $version = 0)
    {
        $this->eventRecorder = new DefaultEventRecorder($version);
    }

    public static function rehydrate(AggregateId $aggregateId, object $state, iterable $events): self
    {
        $self = new self($state, $aggregateId);
        foreach ($events as $event) {
            $self->apply($event, false);
        }

        return $self;
    }

    public function applyAndRecord(object $event): void
    {
        $this->apply($event, true);
    }

    private function apply(object $event, bool $record): void
    {
        $method = 'when' . new \ReflectionClass($event)->getShortName();

        $ref = new \ReflectionObject($this->state);
        if (! $ref->hasMethod($method)) {
            throw new \RuntimeException("Missing method $method in state " . $ref->getName());
        }

        $ref->getMethod($method)->invoke($this->state, $event);

        if ($record) {
            $this->eventRecorder->record($event);
        }
    }
}
