<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Verification;

use PpPhp\Core\Platform\Event\Contracts\EventInterface;
use PpPhp\Core\Platform\Event\Contracts\EventBusInterface;
use PpPhp\Core\Platform\Event\Contracts\EventDispatcherInterface;
use PpPhp\Core\Platform\Event\Contracts\EventListenerInterface;
use PpPhp\Core\Platform\Event\Event;
use PpPhp\Core\Platform\Event\EventBus;
use PpPhp\Core\Platform\Event\EventDispatcher;
use PpPhp\Core\Platform\Event\EventEnvelope;
use PpPhp\Core\Platform\Event\Listener\EventListenerDefinition;
use PpPhp\Core\Platform\Event\Listener\ListenerRepository;
use PpPhp\Core\Platform\Event\Validation\EventModelAuditor;

final class EventCoreVerifier
{
    public function verify(string $basePath): EventCoreStatus
    {
        $checks = [];
        $issues = [];

        $checks['contracts'] = interface_exists(EventInterface::class)
            && interface_exists(EventBusInterface::class)
            && interface_exists(EventDispatcherInterface::class)
            && interface_exists(EventListenerInterface::class);

        if (!$checks['contracts']) {
            $issues[] = ['check' => 'contracts', 'message' => 'One or more Event contracts are missing.'];
        }

        $checks['coreClasses'] = class_exists(Event::class)
            && class_exists(EventBus::class)
            && class_exists(EventDispatcher::class)
            && class_exists(EventEnvelope::class)
            && class_exists(EventListenerDefinition::class)
            && class_exists(ListenerRepository::class);

        if (!$checks['coreClasses']) {
            $issues[] = ['check' => 'coreClasses', 'message' => 'One or more Event core classes are missing.'];
        }

        $auditIssues = (new EventModelAuditor())->audit($basePath);
        $checks['auditor'] = $auditIssues === [];
        foreach ($auditIssues as $issue) {
            $issues[] = [
                'check' => 'auditor',
                'message' => $issue['message'] ?? 'Audit issue',
                'file' => $issue['file'] ?? '',
            ];
        }

        $listenerCount = 0;
        $handled = 0;
        $propagationHandled = 0;

        try {
            $bus = new EventBus();
            $order = [];

            $bus->listen('platform.event.verify', static function (EventInterface $event) use (&$order): void {
                $order[] = 'low';
            }, 0);

            $bus->listen('platform.event.verify', static function (EventInterface $event) use (&$order): void {
                $order[] = 'high';
            }, 100);

            $envelope = $bus->dispatch('platform.event.verify', ['verify' => true], ['source' => 'event-core-verifier']);
            $listenerCount = $bus->repository()->count();
            $handled = count($envelope->handledBy());

            $checks['dispatch'] = $envelope->ok() && $handled === 2;
            $checks['priorities'] = $order === ['high', 'low'];

            if (!$checks['dispatch']) {
                $issues[] = ['check' => 'dispatch', 'message' => 'Event dispatch failed or did not invoke all listeners.'];
            }

            if (!$checks['priorities']) {
                $issues[] = ['check' => 'priorities', 'message' => 'Listener priority order failed.', 'order' => $order];
            }

            $stopBus = new EventBus();
            $count = 0;

            $stopBus->listen('platform.event.stop', static function (Event $event) use (&$count): void {
                $count++;
                $event->stopPropagation();
            }, 100);

            $stopBus->listen('platform.event.stop', static function (Event $event) use (&$count): void {
                $count++;
            }, 0);

            $stopEnvelope = $stopBus->dispatch(new Event('platform.event.stop'));
            $propagationHandled = count($stopEnvelope->handledBy());

            $checks['propagation'] = $count === 1 && $propagationHandled === 1;

            if (!$checks['propagation']) {
                $issues[] = ['check' => 'propagation', 'message' => 'Propagation stop failed.'];
            }
        } catch (\Throwable $e) {
            $checks['dispatch'] = false;
            $checks['priorities'] = false;
            $checks['propagation'] = false;
            $issues[] = ['check' => 'runtime', 'message' => $e->getMessage()];
        }

        $failed = count(array_filter($checks, static fn (bool $ok): bool => !$ok));
        $score = max(0, 100 - ($failed * 20));

        return new EventCoreStatus(!in_array(false, $checks, true), $checks, [
            'listeners' => $listenerCount,
            'handled' => $handled,
            'propagationHandled' => $propagationHandled,
            'auditIssues' => count($auditIssues),
            'score' => $score,
            'basePath' => $basePath,
        ], $issues);
    }
}
