<?php

declare(strict_types=1);

namespace Tests\Support\OpsUx;

use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;

final class SourceFileScanner
{
    /**
     * @param  list<string>  $roots
     * @param  list<string>  $excludedAbsolutePaths
     * @return list<string>
     */
    public static function phpFiles(array $roots, array $excludedAbsolutePaths = []): array
    {
        $files = [];
        $excluded = array_fill_keys(array_map(self::normalizePath(...), $excludedAbsolutePaths), true);

        foreach ($roots as $root) {
            $root = self::normalizePath($root);

            if (! is_dir($root)) {
                continue;
            }

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

            /** @var SplFileInfo $file */
            foreach ($iterator as $file) {
                if (! $file->isFile()) {
                    continue;
                }

                $path = self::normalizePath($file->getPathname());

                if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') {
                    continue;
                }

                if (isset($excluded[$path])) {
                    continue;
                }

                $files[] = $path;
            }
        }

        sort($files);

        return array_values(array_unique($files));
    }

    public static function projectRoot(): string
    {
        return self::normalizePath(dirname(__DIR__, 3));
    }

    public static function relativePath(string $absolutePath): string
    {
        $absolutePath = self::normalizePath($absolutePath);
        $root = self::projectRoot();

        if (str_starts_with($absolutePath, $root.'/')) {
            return substr($absolutePath, strlen($root) + 1);
        }

        return $absolutePath;
    }

    public static function read(string $path): string
    {
        return (string) file_get_contents($path);
    }

    public static function snippet(string $source, int $line, int $contextLines = 2): string
    {
        $allLines = preg_split('/\R/', $source) ?: [];
        $line = max(1, $line);

        $start = max(1, $line - $contextLines);
        $end = min(count($allLines), $line + $contextLines);

        $snippet = [];

        for ($index = $start; $index <= $end; $index++) {
            $prefix = $index === $line ? '>' : ' ';
            $snippet[] = sprintf('%s%4d | %s', $prefix, $index, $allLines[$index - 1] ?? '');
        }

        return implode("\n", $snippet);
    }

    private static function normalizePath(string $path): string
    {
        return str_replace('\\', '/', $path);
    }
}
