<?php

declare(strict_types=1);

namespace App\Framework\Database\Events;

use App\Framework\Core\Events\EventDispatcher;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\DateTime\Clock;

/**
 * Manages entity lifecycle events and coordinates with the core event system
 */
final class EntityEventManager
{
    public function __construct(
        private readonly EventDispatcher $eventDispatcher,
        private readonly DomainEventCollector $domainEventCollector,
        private readonly Clock $clock
    ) {
    }

    /**
     * Dispatch an entity lifecycle event
     */
    public function dispatchLifecycleEvent(object $event): void
    {
        $this->eventDispatcher->dispatch($event);
    }

    /**
     * Record a domain event for later dispatch
     */
    public function recordDomainEvent(object $entity, object $event): void
    {
        $this->domainEventCollector->recordEvent($entity, $event);
    }

    /**
     * Dispatch all domain events for an entity and clear them
     */
    public function dispatchDomainEventsForEntity(object $entity): void
    {
        $events = $this->domainEventCollector->getEventsForEntity($entity);

        foreach ($events as $event) {
            $this->eventDispatcher->dispatch($event);
        }

        $this->domainEventCollector->clearEventsForEntity($entity);
    }

    /**
     * Dispatch all domain events across all entities and clear them
     */
    public function dispatchAllDomainEvents(): void
    {
        $allEvents = $this->domainEventCollector->getAllEvents();

        foreach ($allEvents as $event) {
            $this->eventDispatcher->dispatch($event);
        }

        // Events are automatically cleared when entities are garbage collected
        // But we can manually clear for immediate cleanup
        foreach ($this->domainEventCollector->getAllEvents() as $entity) {
            $this->domainEventCollector->clearEventsForEntity($entity);
        }
    }

    /**
     * Create and dispatch an EntityCreatedEvent
     */
    public function entityCreated(object $entity, string $entityClass, mixed $entityId, array $data = []): void
    {
        $timestamp = Timestamp::fromClock($this->clock);
        $event = new EntityCreatedEvent($entity, $entityClass, $entityId, $data, $timestamp);
        $this->dispatchLifecycleEvent($event);
    }

    /**
     * Create and dispatch an EntityUpdatedEvent
     */
    public function entityUpdated(
        object $entity,
        string $entityClass,
        mixed $entityId,
        array $changes = [],
        array $oldValues = [],
        array $newValues = []
    ): void {
        $timestamp = Timestamp::fromClock($this->clock);
        $event = new EntityUpdatedEvent($entity, $entityClass, $entityId, $changes, $oldValues, $newValues, $timestamp);
        $this->dispatchLifecycleEvent($event);
    }

    /**
     * Create and dispatch an EntityDeletedEvent
     */
    public function entityDeleted(object $entity, string $entityClass, mixed $entityId, array $deletedData = []): void
    {
        $timestamp = Timestamp::fromClock($this->clock);
        $event = new EntityDeletedEvent($entity, $entityClass, $entityId, $deletedData, $timestamp);
        $this->dispatchLifecycleEvent($event);
    }

    /**
     * Create and dispatch an EntityLoadedEvent
     */
    public function entityLoaded(
        object $entity,
        string $entityClass,
        mixed $entityId,
        array $loadedData = [],
        bool $wasLazy = false
    ): void {
        $timestamp = Timestamp::fromClock($this->clock);
        $event = new EntityLoadedEvent($entity, $entityClass, $entityId, $loadedData, $wasLazy, $timestamp);
        $this->dispatchLifecycleEvent($event);
    }

    /**
     * Create and dispatch an EntityDetachedEvent
     */
    public function entityDetached(object $entity, string $entityClass, mixed $entityId): void
    {
        $timestamp = Timestamp::fromClock($this->clock);
        $event = new EntityDetachedEvent($entity, $entityClass, $entityId, $timestamp);
        $this->dispatchLifecycleEvent($event);
    }

    /**
     * Get domain event collector for advanced usage
     */
    public function getDomainEventCollector(): DomainEventCollector
    {
        return $this->domainEventCollector;
    }

    /**
     * Get core event dispatcher for advanced usage
     */
    public function getEventDispatcher(): EventDispatcher
    {
        return $this->eventDispatcher;
    }

    /**
     * Get statistics about domain events
     */
    public function getDomainEventStats(): array
    {
        return [
            'tracked_entities' => $this->domainEventCollector->getTrackedEntityCount(),
            'total_events' => $this->domainEventCollector->getTotalEventCount(),
        ];
    }
}
