using AxCopilot.Handlers;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Core;

/// <summary>
/// 입력된 텍스트를 파싱하여 적절한 ActionHandler로 라우팅합니다.
/// Prefix 기반 라우팅 테이블을 관리합니다.
/// </summary>
public class CommandResolver
{
    private readonly FuzzyEngine _fuzzy;
    private readonly SettingsService _settings;
    private readonly Dictionary<string, IActionHandler> _handlers = new();
    /// <summary>Prefix = null 핸들러 목록 — 모든 쿼리에 병렬 실행</summary>
    private readonly List<IActionHandler> _fuzzyHandlers = new();

    public CommandResolver(FuzzyEngine fuzzy, SettingsService settings)
    {
        _fuzzy = fuzzy;
        _settings = settings;
    }

    /// <summary>
    /// 핸들러를 등록합니다. 플러그인 로드 시에도 이 메서드를 호출합니다.
    /// </summary>
    public void RegisterHandler(IActionHandler handler)
    {
        // Prefix 없는 핸들러 → 모든 쿼리에 부가 결과 제공 (예: BookmarkHandler)
        if (handler.Prefix == null)
        {
            _fuzzyHandlers.Add(handler);
            LogService.Info($"FuzzyHandler 등록: name='{handler.Metadata.Name}'");
            return;
        }

        if (_handlers.ContainsKey(handler.Prefix))
            LogService.Warn($"Prefix '{handler.Prefix}' 중복 등록: " +
                            $"'{handler.Metadata.Name}'이 기존 핸들러를 덮어씁니다.");

        _handlers[handler.Prefix] = handler;
        LogService.Info($"Handler 등록: prefix='{handler.Prefix}', name='{handler.Metadata.Name}'");
    }

    /// <summary>
    /// 입력 텍스트를 분석하여 결과 목록을 반환합니다.
    /// </summary>
    public async Task<IEnumerable<SDK.LauncherItem>> ResolveAsync(string input, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(input))
            return Enumerable.Empty<SDK.LauncherItem>();

        // 1. Prefix 기반 라우팅
        foreach (var (prefix, handler) in _handlers)
        {
            if (input.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
            {
                var query = input.Length > prefix.Length
                    ? input[prefix.Length..].Trim()
                    : "";

                try
                {
                    return await handler.GetItemsAsync(query, ct);
                }
                catch (OperationCanceledException) { throw; }
                catch (Exception ex)
                {
                    LogService.Error($"Handler '{handler.Metadata.Name}' 오류: {ex.Message}");
                    return [new SDK.LauncherItem($"오류: {ex.Message}", handler.Metadata.Name, null, null)];
                }
            }
        }

        // 2. 경로 쿼리 감지 → 파일 탐색기 단독 처리 (퍼지 검색 우선순위 우회)
        if (FileBrowserHandler.IsPathQuery(input))
        {
            var fb = _fuzzyHandlers.OfType<FileBrowserHandler>().FirstOrDefault();
            if (fb != null)
                return await fb.GetItemsAsync(input, ct);
        }

        // 3. 고급 필터 문법 감지 (ext:, size:, modified:, type:, in:)
        var maxResults = _settings.Settings.Launcher.MaxResults;
        var (cleanQuery, filters) = SearchFilterParser.Parse(input);

        if (filters.HasFilters)
        {
            return BuildFilteredResults(cleanQuery, filters, maxResults);
        }

        // 4. Fuzzy 검색 폴백 + null-prefix 핸들러 병렬 실행
        // Path 기반 중복 제거: 같은 경로의 항목이 여러 키워드로 매칭될 때 첫 번째만 표시
        var seenPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        // SortByUsage에 lazy 시퀀스를 직접 전달 → 중간 ToList 1회 제거
        var fuzzyItems = UsageRankingService.SortByUsage(
            _fuzzy.Search(input, maxResults * 2) // 중복 제거 여유분
                .Where(r => seenPaths.Add(r.Entry.Path)) // Path가 처음 등장할 때만 통과
                .Take(maxResults)
                .Select(r => new SDK.LauncherItem(
                    r.Entry.DisplayName,
                    r.Entry.Type == IndexEntryType.Alias ? r.Entry.AliasType switch
                    {
                        "url"   => "URL 단축키",
                        "batch" => "명령 단축키",
                        _       => r.Entry.Path
                    } : r.Entry.Path + "  ⇧ Shift+Enter: 폴더 열기",
                    IconCacheService.GetIconPath(r.Entry.Path, r.Entry.Type == IndexEntryType.Folder),
                    r.Entry,
                    Symbol: r.Entry.Type switch
                    {
                        IndexEntryType.App    => Symbols.App,
                        IndexEntryType.Folder => Symbols.Folder,
                        IndexEntryType.Alias  => r.Entry.AliasType switch
                        {
                            "url"   => Symbols.Globe,
                            "batch" => Symbols.Terminal,
                            _       => Symbols.Plugin
                        },
                        _                     => Symbols.File
                    },
                    Group: r.Entry.Type switch
                    {
                        IndexEntryType.App    => "앱",
                        IndexEntryType.Folder => "폴더",
                        IndexEntryType.Alias  => "단축키",
                        _                     => "파일"
                    }
                )),
            item => (item.Data as IndexEntry)?.Path
        ).ToList(); // 단일 ToList로 List<LauncherItem> 확정

        // null-prefix 핸들러 결과를 뒤에 추가 (최대 3개씩)
        if (_fuzzyHandlers.Count > 0)
        {
            var extraTasks = _fuzzyHandlers
                .Select(h => SafeGetItemsAsync(h, input, ct))
                .ToList();
            await Task.WhenAll(extraTasks);

            foreach (var task in extraTasks)
            {
                if (task.IsCompletedSuccessfully)
                    fuzzyItems.AddRange(task.Result.Take(3));
            }
        }

        return fuzzyItems;
    }

    /// <summary>
    /// 선택된 항목을 실행합니다.
    /// </summary>
    public async Task ExecuteAsync(SDK.LauncherItem item, string lastInput, CancellationToken ct)
    {
        // Prefix 기반 실행
        foreach (var (prefix, handler) in _handlers)
        {
            if (lastInput.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
            {
                // 명령어 사용 통계 기록 (prefix + 첫 단어)
                var q = lastInput.Length > prefix.Length
                    ? lastInput[prefix.Length..].Trim().Split(' ')[0]
                    : "";
                var cmdKey = string.IsNullOrEmpty(q) ? prefix : $"{prefix}{q}";
                UsageStatisticsService.RecordCommandUsage(cmdKey);

                await handler.ExecuteAsync(item, ct);
                return;
            }
        }

        // null-prefix 핸들러 결과 실행 (Data가 string = URL인 경우)
        if (item.Data is string urlData && urlData.StartsWith("http", StringComparison.OrdinalIgnoreCase))
        {
            await ExecuteNullPrefixAsync(item, ct);
            return;
        }

        // 파일 탐색기 항목 실행 (FileBrowserEntry)
        if (item.Data is FileBrowserEntry)
        {
            await ExecuteNullPrefixAsync(item, ct);
            return;
        }

        // Fuzzy 결과 실행 (IndexEntry 기반)
        if (item.Data is IndexEntry entry)
        {
            var expanded = Environment.ExpandEnvironmentVariables(entry.Path);
            try
            {
                // Process.Start를 먼저 실행하여 체감 속도 확보
                await Task.Run(() =>
                    System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(expanded)
                    {
                        UseShellExecute = true
                    }));
            }
            catch (Exception ex)
            {
                LogService.Error($"실행 실패: {expanded} - {ex.Message}");
            }

            // 통계 기록은 파일 열기 이후 비동기로
            _ = Task.Run(() => UsageRankingService.RecordExecution(entry.Path));
        }
    }

    public IReadOnlyDictionary<string, IActionHandler> RegisteredHandlers => _handlers;

    // ─── 고급 필터 검색 결과 생성 ─────────────────────────────────────────
    private IEnumerable<SDK.LauncherItem> BuildFilteredResults(
        string textQuery, ParsedFilters filters, int maxResults)
    {
        var results = new List<SDK.LauncherItem>();

        // 필터 힌트 항목 (상단 표시 — 어떤 필터가 적용 중인지 안내)
        var hint = SearchFilterParser.Describe(filters);
        results.Add(new SDK.LauncherItem(
            $"필터 적용 중: {hint}",
            string.IsNullOrEmpty(textQuery)
                ? "전체 항목에서 검색 중"
                : $"'{textQuery}' + 필터",
            null, null,
            Symbol: "\uE16E", // Filter 아이콘 (MDL2)
            Group: "필터"));

        // 퍼지 + 필터 적용 (size/modified는 파일시스템 접근이 있으므로 Task.Run에서 실행)
        var filtered = _fuzzy.SearchWithFilter(
            textQuery,
            e => SearchFilterParser.Matches(e, filters),
            maxResults);

        var seenPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        foreach (var r in filtered)
        {
            if (!seenPaths.Add(r.Entry.Path)) continue;

            // 크기/수정일 부가 정보 — 파일 항목에만 추가
            string sub = r.Entry.Path + "  ⇧ Shift+Enter: 폴더 열기";
            if (r.Entry.Type == IndexEntryType.File &&
                (filters.SizeBytes != null || filters.ModifiedFrom != null))
            {
                try
                {
                    var fi = new System.IO.FileInfo(
                        Environment.ExpandEnvironmentVariables(r.Entry.Path));
                    if (fi.Exists)
                    {
                        var sizeTxt = FormatBytes(fi.Length);
                        var dateTxt = fi.LastWriteTime.ToString("yyyy-MM-dd");
                        sub = $"{sizeTxt}  ·  {dateTxt}  ·  {r.Entry.Path}";
                    }
                }
                catch { /* 접근 실패 시 기본 subtitle */ }
            }

            results.Add(new SDK.LauncherItem(
                r.Entry.DisplayName,
                sub,
                null,
                r.Entry,
                Symbol: r.Entry.Type switch
                {
                    IndexEntryType.App    => Symbols.App,
                    IndexEntryType.Folder => Symbols.Folder,
                    _                     => Symbols.File
                },
                Group: "검색 결과"));
        }

        if (results.Count == 1) // 힌트 항목만 있음 → 결과 없음 메시지
        {
            results.Add(new SDK.LauncherItem(
                "검색 결과 없음",
                $"필터 조건을 확인하세요: {hint}",
                null, null,
                Symbol: Symbols.Error,
                Group: "검색 결과"));
        }

        return results;
    }

    private static string FormatBytes(long b) => b switch
    {
        >= 1024L * 1024 * 1024 => $"{b / (1024.0 * 1024 * 1024):F1}GB",
        >= 1024L * 1024        => $"{b / (1024.0 * 1024):F1}MB",
        >= 1024L               => $"{b / 1024.0:F1}KB",
        _                      => $"{b}B"
    };

    // null-prefix 핸들러 실행 (ExecuteAsync 라우팅)
    public async Task ExecuteNullPrefixAsync(SDK.LauncherItem item, CancellationToken ct)
    {
        foreach (var handler in _fuzzyHandlers)
        {
            try { await handler.ExecuteAsync(item, ct); return; }
            catch (OperationCanceledException) { throw; }
            catch (Exception ex)
            {
                LogService.Error($"FuzzyHandler '{handler.Metadata.Name}' 실행 오류: {ex.Message}");
            }
        }
    }

    private static async Task<IEnumerable<SDK.LauncherItem>> SafeGetItemsAsync(
        IActionHandler handler, string query, CancellationToken ct)
    {
        try
        {
            return await handler.GetItemsAsync(query, ct);
        }
        catch (OperationCanceledException) { throw; }
        catch (Exception ex)
        {
            LogService.Error($"FuzzyHandler '{handler.Metadata.Name}' 오류: {ex.Message}");
            return Enumerable.Empty<SDK.LauncherItem>();
        }
    }
}
