namespace ClaudeDo.Ui.Services;

public sealed record RepoCandidate(string Name, string FullPath);

public static class RepoScanner
{
    private const int MaxDepth = 5;

    private static readonly HashSet<string> SkipDirNames = new(StringComparer.OrdinalIgnoreCase)
    {
        "node_modules", "bin", "obj", ".git", ".vs", "packages",
    };

    public static IReadOnlyList<RepoCandidate> Scan(string parentFolder)
    {
        if (string.IsNullOrWhiteSpace(parentFolder) || !Directory.Exists(parentFolder))
            return Array.Empty<RepoCandidate>();

        try
        {
            var info = new DirectoryInfo(parentFolder);
            if (info.Attributes.HasFlag(FileAttributes.ReparsePoint))
                return Array.Empty<RepoCandidate>();
        }
        catch (Exception e) when (e is IOException or UnauthorizedAccessException)
        { return Array.Empty<RepoCandidate>(); }

        var result = new List<RepoCandidate>();
        ScanDirectory(parentFolder, depth: 0, result);
        return result;
    }

    private static void ScanDirectory(string dir, int depth, List<RepoCandidate> result)
    {
        if (IsRepo(dir))
        {
            result.Add(new RepoCandidate(Path.GetFileName(dir), dir));
            return;
        }

        if (depth >= MaxDepth)
            return;

        IEnumerable<string> subdirs;
        try { subdirs = Directory.EnumerateDirectories(dir); }
        catch (Exception e) when (e is IOException or UnauthorizedAccessException)
        { return; }

        foreach (var subdir in subdirs)
        {
            var name = Path.GetFileName(subdir);
            if (SkipDirNames.Contains(name))
                continue;

            try
            {
                var info = new DirectoryInfo(subdir);
                if (info.Attributes.HasFlag(FileAttributes.ReparsePoint))
                    continue;
            }
            catch (Exception e) when (e is IOException or UnauthorizedAccessException)
            { continue; }

            try { ScanDirectory(subdir, depth + 1, result); }
            catch (Exception e) when (e is IOException or UnauthorizedAccessException)
            { }
        }
    }

    private static bool IsRepo(string dir)
    {
        var gitPath = Path.Combine(dir, ".git");
        return Directory.Exists(gitPath) || File.Exists(gitPath);
    }
}
