<?php

declare(strict_types=1);

namespace PpPhp\Core\Discovery;

use PpPhp\Core\Discovery\Contracts\DiscoveryProviderInterface;
use PpPhp\Core\Discovery\Providers\ComponentDiscoveryProvider;
use PpPhp\Core\Discovery\Providers\DocumentationDiscoveryProvider;
use PpPhp\Core\Discovery\Providers\ModuleDiscoveryProvider;
use PpPhp\Core\Discovery\Providers\PackageDiscoveryProvider;
use PpPhp\Core\Discovery\Providers\PluginDiscoveryProvider;
use PpPhp\Core\Discovery\Providers\ThemeDiscoveryProvider;

final class DiscoveryEngine
{
    /** @var DiscoveryProviderInterface[] */
    private array $providers = [];

    private DiscoveryRegistry $registry;

    public function __construct()
    {
        $this->registry = new DiscoveryRegistry();
    }

    public static function default(): self
    {
        $engine = new self();

        $engine->addProvider(new PackageDiscoveryProvider());
        $engine->addProvider(new ThemeDiscoveryProvider());
        $engine->addProvider(new ModuleDiscoveryProvider());
        $engine->addProvider(new PluginDiscoveryProvider());
        $engine->addProvider(new ComponentDiscoveryProvider());
        $engine->addProvider(new DocumentationDiscoveryProvider());

        return $engine;
    }

    public function addProvider(DiscoveryProviderInterface $provider): void
    {
        $this->providers[$provider->id()] = $provider;

        uasort(
            $this->providers,
            static fn (DiscoveryProviderInterface $a, DiscoveryProviderInterface $b): int => $a->priority() <=> $b->priority()
        );
    }

    public function discover(string $basePath): DiscoveryResult
    {
        $final = new DiscoveryResult();

        foreach ($this->providers as $provider) {
            $result = $provider->discover($basePath);
            $final->merge($result);

            foreach ($result->items() as $item) {
                $this->registry->register($item);
            }
        }

        return $final;
    }

    public function registry(): DiscoveryRegistry
    {
        return $this->registry;
    }

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