using System.IO;

namespace AxCopilot.Services;

public partial class IndexService
{
    // ─── 경로 탐색 헬퍼 + 검색 캐시 ──────────────────────────────────────────

    private static string? FindOfficeApp(string exeName)
    {
        var roots = new[]
        {
            @"C:\Program Files\Microsoft Office\root\Office16",
            @"C:\Program Files (x86)\Microsoft Office\root\Office16",
            @"C:\Program Files\Microsoft Office\Office16",
            @"C:\Program Files (x86)\Microsoft Office\Office16",
        };
        foreach (var root in roots)
        {
            var path = Path.Combine(root, exeName);
            if (File.Exists(path)) return path;
        }
        return null;
    }

    private static string? FindTeams()
    {
        var localApp = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        var path = Path.Combine(localApp, @"Microsoft\Teams\current\Teams.exe");
        if (File.Exists(path)) return path;
        // New Teams (MSIX)
        var msTeams = Path.Combine(localApp, @"Microsoft\WindowsApps\ms-teams.exe");
        return File.Exists(msTeams) ? msTeams : null;
    }

    private static string? FindInPath(string fileName)
    {
        var pathEnv = Environment.GetEnvironmentVariable("PATH") ?? "";
        foreach (var dir in pathEnv.Split(';'))
        {
            if (string.IsNullOrWhiteSpace(dir)) continue;
            var full = Path.Combine(dir.Trim(), fileName);
            if (File.Exists(full)) return full;
        }
        return null;
    }

    private static string? FindInLocalAppData(string relativePath)
    {
        var localApp = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        var path = Path.Combine(localApp, relativePath);
        return File.Exists(path) ? path : null;
    }

    /// <summary>ProgramFiles / ProgramFiles(x86) 두 곳을 탐색합니다.</summary>
    private static string? FindInProgramFiles(string relativePath)
    {
        var pf   = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
        var pf86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
        var p1 = Path.Combine(pf,   relativePath);
        if (File.Exists(p1)) return p1;
        var p2 = Path.Combine(pf86, relativePath);
        return File.Exists(p2) ? p2 : null;
    }

    /// <summary>AppData\Roaming 경로를 탐색합니다.</summary>
    private static string? FindInRoaming(string relativePath)
    {
        var roaming = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
        var path = Path.Combine(roaming, relativePath);
        return File.Exists(path) ? path : null;
    }

    // ─── 검색 가속 캐시 계산 ──────────────────────────────────────────────────

    /// <summary>빌드 완료 후 전체 항목의 검색 캐시를 한 번에 계산합니다.</summary>
    private static void ComputeAllSearchCaches(List<IndexEntry> entries)
    {
        foreach (var e in entries)
            ComputeSearchCache(e);
    }

    /// <summary>항목 1개의 NameLower / NameJamo / NameChosung 캐시를 계산합니다.</summary>
    private static void ComputeSearchCache(IndexEntry entry)
    {
        entry.NameLower = entry.Name.ToLowerInvariant();
        // 자모 분리 (FuzzyEngine static 메서드 — 동일 어셈블리 internal 접근)
        entry.NameJamo = AxCopilot.Core.FuzzyEngine.DecomposeToJamo(entry.NameLower);
        // 초성 문자열 (예: "엑셀" → "ㅇㅅ", "메모장" → "ㅁㅁㅈ")
        var sb = new System.Text.StringBuilder(entry.NameLower.Length);
        foreach (var c in entry.NameLower)
        {
            var cho = AxCopilot.Core.FuzzyEngine.GetChosung(c);
            if (cho != '\0') sb.Append(cho);
        }
        entry.NameChosung = sb.ToString();
    }

    /// <summary>인덱싱 속도에 따른 throttle 간격(ms). N개 파일마다 yield.</summary>
    private static (int batchSize, int delayMs) GetThrottle(string speed) => speed switch
    {
        "fast" => (500, 0),    // 최대 속도, CPU 양보 없음
        "slow" => (50, 15),    // 50개마다 15ms 양보 → PC 부하 최소
        _      => (150, 5),    // normal: 150개마다 5ms → 적정 균형
    };

    private static async Task ScanDirectoryAsync(string dir, List<IndexEntry> entries,
        HashSet<string> allowedExts, string indexSpeed, CancellationToken ct)
    {
        var (batchSize, delayMs) = GetThrottle(indexSpeed);

        await Task.Run(async () =>
        {
            try
            {
                int count = 0;

                // 파일 인덱싱 (확장자 필터 적용)
                foreach (var file in Directory.EnumerateFiles(dir, "*.*", SearchOption.AllDirectories))
                {
                    ct.ThrowIfCancellationRequested();
                    var ext = Path.GetExtension(file).ToLowerInvariant();
                    if (allowedExts.Count > 0 && !allowedExts.Contains(ext)) continue;
                    var name = Path.GetFileNameWithoutExtension(file);
                    if (string.IsNullOrEmpty(name)) continue;
                    var type = ext switch
                    {
                        ".exe" => IndexEntryType.App,
                        ".lnk" or ".url" => IndexEntryType.File,
                        _ => IndexEntryType.File
                    };
                    entries.Add(new IndexEntry
                    {
                        Name = name,
                        DisplayName = ext is ".exe" or ".lnk" or ".url" ? name : name + ext,
                        Path = file,
                        Type = type
                    });

                    // 속도 조절: batchSize개마다 CPU 양보
                    if (delayMs > 0 && ++count % batchSize == 0)
                        await Task.Delay(delayMs, ct);
                }

                // 폴더 인덱싱 (1단계 하위 폴더)
                foreach (var subDir in Directory.EnumerateDirectories(dir, "*", SearchOption.TopDirectoryOnly))
                {
                    ct.ThrowIfCancellationRequested();
                    var name = Path.GetFileName(subDir);
                    if (name.StartsWith(".")) continue;
                    entries.Add(new IndexEntry
                    {
                        Name = name,
                        DisplayName = name,
                        Path = subDir,
                        Type = IndexEntryType.Folder
                    });
                }
            }
            catch (UnauthorizedAccessException ex)
            {
                LogService.Warn($"폴더 접근 불가 (건너뜀): {dir} - {ex.Message}");
            }
        }, ct);
    }
}
