<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Discovery;

use PpPhp\Core\Platform\Event\Attributes\EventListener;
use PpPhp\Core\Platform\Event\Registry\EventListenerRegistry;
use ReflectionClass;
use Throwable;

final class AttributeListenerScanner
{
    private array $warnings = [];

    public function scanClasses(EventListenerRegistry $registry, array $classes): int
    {
        $count = 0;

        foreach ($classes as $className) {
            try {
                if (!class_exists($className)) {
                    continue;
                }

                $reflection = new ReflectionClass($className);

                if (!$reflection->isInstantiable()) {
                    continue;
                }

                $instance = null;

                foreach ($reflection->getAttributes(EventListener::class) as $attribute) {
                    $listener = $attribute->newInstance();
                    $instance ??= $reflection->newInstance();
                    if (method_exists($instance, 'handle')) {
                        $registry->register($listener->event, [$instance, 'handle'], $listener->priority, $listener->tags, [
                            'source' => 'attribute-class',
                            'class' => $className,
                        ], $listener->enabled, $listener->once);
                        $count++;
                    }
                }

                foreach ($reflection->getMethods() as $method) {
                    foreach ($method->getAttributes(EventListener::class) as $attribute) {
                        $listener = $attribute->newInstance();
                        $instance ??= $reflection->newInstance();
                        $registry->register($listener->event, [$instance, $method->getName()], $listener->priority, $listener->tags, [
                            'source' => 'attribute-method',
                            'class' => $className,
                            'method' => $method->getName(),
                        ], $listener->enabled, $listener->once);
                        $count++;
                    }
                }
            } catch (Throwable $e) {
                $this->warnings[] = ['class' => (string) $className, 'message' => $e->getMessage()];
            }
        }

        return $count;
    }

    public function warnings(): array
    {
        return $this->warnings;
    }
}
