<?php

declare(strict_types=1);

namespace PpPhp\Core\Discovery\Providers;

use PpPhp\Core\Discovery\Contracts\DiscoveryProviderInterface;
use PpPhp\Core\Discovery\DiscoveryItem;
use PpPhp\Core\Discovery\DiscoveryResult;
use PpPhp\Core\Discovery\ManifestReader;

abstract class AbstractManifestDiscoveryProvider implements DiscoveryProviderInterface
{
    public function priority(): int
    {
        return 100;
    }

    public function discover(string $basePath): DiscoveryResult
    {
        $result = new DiscoveryResult();
        $root = rtrim($basePath, '/') . '/' . trim($this->relativeRoot(), '/');

        if (!is_dir($root)) {
            return $result;
        }

        foreach ($this->manifestFiles($root) as $manifestFile) {
            $manifest = ManifestReader::read($manifestFile);

            if ($manifest === []) {
                $result->error('Invalid or empty manifest: ' . $manifestFile);
                continue;
            }

            $id = ManifestReader::idFromManifest($manifest, basename(dirname($manifestFile)));

            $result->add(new DiscoveryItem(
                type: $this->type(),
                id: $id,
                path: dirname($manifestFile),
                metadata: $manifest
            ));
        }

        return $result;
    }

    /**
     * @return string[]
     */
    protected function manifestFiles(string $root): array
    {
        $files = [];

        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($root, \FilesystemIterator::SKIP_DOTS)
        );

        foreach ($iterator as $file) {
            if (!$file instanceof \SplFileInfo) {
                continue;
            }

            if ($file->isFile() && $file->getFilename() === $this->manifestName()) {
                $files[] = $file->getPathname();
            }
        }

        sort($files);

        return $files;
    }

    abstract protected function relativeRoot(): string;

    abstract protected function manifestName(): string;

    abstract protected function type(): string;
}
