<?php

declare(strict_types=1);

namespace PpPhp\Core\Manifest;

final class ManifestDiscovery
{
    public function __construct(
        private readonly ManifestReader $reader = new ManifestReader(),
        private readonly ManifestValidator $validator = new ManifestValidator()
    ) {
    }

    public function discover(string $basePath): ManifestDiscoveryResult
    {
        $result = new ManifestDiscoveryResult();

        foreach ($this->manifestFiles($basePath) as $manifestFile) {
            $manifest = $this->reader->tryRead($manifestFile);

            if (!$manifest) {
                $result->error('Unable to read manifest: ' . $manifestFile);
                continue;
            }

            $errors = $this->validator->validate($manifest);

            if ($errors !== []) {
                foreach ($errors as $error) {
                    $result->error($manifestFile . ': ' . $error);
                }
                continue;
            }

            $result->add($manifest);
        }

        return $result;
    }

    /**
     * @return array<int, string>
     */
    private function manifestFiles(string $basePath): array
    {
        $files = [];

        if (!is_dir($basePath)) {
            return $files;
        }

        $skip = [
            DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR,
            DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR,
            DIRECTORY_SEPARATOR . '.git' . DIRECTORY_SEPARATOR,
            DIRECTORY_SEPARATOR . 'node_modules' . DIRECTORY_SEPARATOR,
        ];

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

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

            $path = $file->getPathname();

            foreach ($skip as $fragment) {
                if (str_contains($path, $fragment)) {
                    continue 2;
                }
            }

            if ($file->getFilename() === 'manifest.json') {
                $files[] = $path;
            }
        }

        sort($files);

        return $files;
    }
}
