<?php

namespace MintyPHP\App\Module;

use MintyPHP\App\AppContainer;
use MintyPHP\App\Module\Contracts\EventListener;
use MintyPHP\Http\RequestContext;
use MintyPHP\Service\Audit\AuditRecorderInterface;

/**
 * Fire-and-forget event dispatcher for module lifecycle events.
 *
 * Listeners are declared in module manifests and lazy-resolved from the container.
 * A failing listener does not break the dispatch chain — the exception is swallowed
 * so that one misbehaving module cannot disrupt core flows.
 */
final class ModuleEventDispatcher
{
    /**
     * @param array<string, list<array{class: class-string, method: string}>> $listenerMap
     */
    public function __construct(
        private readonly array $listenerMap,
        private readonly AppContainer $container,
        private readonly ?AuditRecorderInterface $systemAuditService = null
    ) {
    }

    /**
     * @param array<string, mixed> $payload
     */
    public function dispatch(string $event, array $payload): void
    {
        $listeners = $this->listenerMap[$event] ?? [];

        foreach ($listeners as $descriptor) {
            $class = '';
            $method = '';
            try {
                $class = $descriptor['class'];
                $method = $descriptor['method'];

                $listener = ModuleClassResolver::resolve($this->container, $class);
                if (!$listener instanceof EventListener) {
                    continue;
                }

                $listener->{$method}($event, $payload);
            } catch (\Throwable $exception) {
                $this->recordListenerFailure($event, $class, $method, $exception);
            }
        }
    }

    private function recordListenerFailure(
        string $event,
        string $listenerClass,
        string $listenerMethod,
        \Throwable $exception
    ): void {
        if ($this->systemAuditService === null) {
            return;
        }

        $this->systemAuditService->record('module.listener.failed', 'failed', [
            'metadata' => [
                'module_event' => $event,
                'listener_class' => $listenerClass,
                'listener_method' => $listenerMethod,
                'exception_class' => $exception::class,
                'request_id' => RequestContext::id(),
            ],
        ]);
    }
}
