using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// L17-3: YAML 파서·포맷터·검증 핸들러. "yaml" 프리픽스로 사용합니다.
///
/// 예: yaml                   → 클립보드 YAML 구조 분석
///     yaml validate          → YAML 유효성 검사
///     yaml keys              → 최상위 키 목록
///     yaml get key.subkey    → 특정 경로 값 조회 (점 표기법)
///     yaml stats             → 줄 수·키 수·깊이 통계
///     yaml flat              → 점 표기법으로 평탄화
/// Enter → 결과 복사.
/// 외부 라이브러리 없이 순수 파싱 구현 (기본 YAML 스펙 지원).
/// </summary>
public partial class YamlHandler : IActionHandler
{
    public string? Prefix => "yaml";

    public PluginMetadata Metadata => new(
        "YAML",
        "YAML 파서·검증 — 키 조회 · 구조 분석 · 평탄화",
        "1.0",
        "AX");

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim();
        var items = new List<LauncherItem>();

        string? clipboard = null;
        try
        {
            System.Windows.Application.Current.Dispatcher.Invoke(() =>
            {
                if (Clipboard.ContainsText())
                    clipboard = Clipboard.GetText();
            });
        }
        catch { /* 클립보드 접근 실패 */ }

        if (string.IsNullOrWhiteSpace(q))
        {
            items.Add(new LauncherItem("YAML 파서·분석기",
                "클립보드 YAML 분석  ·  yaml validate / keys / get key / stats / flat",
                null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("yaml validate",    "YAML 유효성 검사",        null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("yaml keys",        "최상위 키 목록",           null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("yaml get key",     "특정 키 값 조회",          null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("yaml stats",       "줄·키·깊이 통계",          null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("yaml flat",        "점 표기법 평탄화",         null, null, Symbol: "\uE8A5"));

            if (string.IsNullOrWhiteSpace(clipboard))
            {
                items.Add(new LauncherItem("클립보드가 비어 있습니다",
                    "YAML 텍스트를 복사한 뒤 사용하세요", null, null, Symbol: "\uE946"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }

            var stat = QuickStat(clipboard);
            items.Add(new LauncherItem("── 클립보드 미리보기 ──", stat, null, ("copy", stat), Symbol: "\uE8A5"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        if (string.IsNullOrWhiteSpace(clipboard))
        {
            items.Add(new LauncherItem("클립보드가 비어 있습니다",
                "YAML 텍스트를 복사한 뒤 사용하세요", null, null, Symbol: "\uE946"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var parts = q.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
        var sub   = parts[0].ToLowerInvariant();

        // 파싱
        var (yamlObj, parseError) = ParseYaml(clipboard);

        switch (sub)
        {
            case "validate":
            case "check":
            case "lint":
            {
                if (parseError != null)
                {
                    items.Add(new LauncherItem("❌ YAML 오류", parseError, null, null, Symbol: "\uE783"));
                }
                else
                {
                    var linesCount = clipboard.Split('\n').Length;
                    var keyCount   = CountKeys(yamlObj);
                    items.Add(new LauncherItem("✓ 유효한 YAML",
                        $"{linesCount}줄  ·  키 {keyCount}개", null, ("copy", "Valid YAML"), Symbol: "\uE8A5"));
                }
                break;
            }

            case "keys":
            case "key":
            {
                if (parseError != null)
                {
                    items.Add(new LauncherItem("YAML 파싱 오류", parseError, null, null, Symbol: "\uE783"));
                    break;
                }
                if (yamlObj is Dictionary<string, object?> dict)
                {
                    items.Add(new LauncherItem($"최상위 키  {dict.Count}개", "", null, null, Symbol: "\uE8A5"));
                    foreach (var (k, v) in dict)
                    {
                        var valStr = FormatValue(v);
                        items.Add(new LauncherItem(k, valStr.Length > 60 ? valStr[..60] + "…" : valStr,
                            null, ("copy", k), Symbol: "\uE8A5"));
                    }
                }
                else items.Add(new LauncherItem("최상위가 매핑이 아닙니다", "배열 또는 스칼라 값", null, null, Symbol: "\uE946"));
                break;
            }

            case "get":
            {
                var keyPath = parts.Length > 1 ? parts[1].Trim() : "";
                if (string.IsNullOrWhiteSpace(keyPath))
                {
                    items.Add(new LauncherItem("키 경로 입력", "예: yaml get server.port", null, null, Symbol: "\uE783"));
                    break;
                }
                if (parseError != null)
                {
                    items.Add(new LauncherItem("YAML 파싱 오류", parseError, null, null, Symbol: "\uE783"));
                    break;
                }
                var found = GetByPath(yamlObj, keyPath.Split('.'));
                if (found == null)
                    items.Add(new LauncherItem($"'{keyPath}' 없음", "경로가 존재하지 않습니다", null, null, Symbol: "\uE946"));
                else
                {
                    var valStr = FormatValue(found);
                    items.Add(new LauncherItem(keyPath, valStr, null, ("copy", valStr), Symbol: "\uE8A5"));
                }
                break;
            }

            case "stats":
            case "stat":
            {
                var lines  = clipboard.Split('\n');
                var blank  = lines.Count(l => string.IsNullOrWhiteSpace(l));
                var comments = lines.Count(l => l.TrimStart().StartsWith('#'));
                var keyLines = lines.Count(l => KeyLineRegex().IsMatch(l));
                var maxDepth = GetMaxDepth(clipboard);
                var keyCount = parseError == null ? CountKeys(yamlObj) : -1;

                items.Add(new LauncherItem($"YAML 통계", $"{lines.Length}줄  ·  키 {keyCount}개  ·  깊이 {maxDepth}",
                    null, null, Symbol: "\uE8A5"));
                items.Add(new LauncherItem("전체 줄",      $"{lines.Length}줄",                          null, ("copy", $"{lines.Length}"), Symbol: "\uE8A5"));
                items.Add(new LauncherItem("빈 줄",        $"{blank}줄",                                 null, ("copy", $"{blank}"), Symbol: "\uE8A5"));
                items.Add(new LauncherItem("주석 줄",      $"{comments}줄",                              null, ("copy", $"{comments}"), Symbol: "\uE8A5"));
                items.Add(new LauncherItem("키 줄",        $"{keyLines}줄",                              null, ("copy", $"{keyLines}"), Symbol: "\uE8A5"));
                items.Add(new LauncherItem("최대 들여쓰기 깊이", $"{maxDepth}단계",                      null, ("copy", $"{maxDepth}"), Symbol: "\uE8A5"));
                if (parseError == null && keyCount >= 0)
                    items.Add(new LauncherItem("전체 키 수 (재귀)", $"{keyCount}개",                     null, ("copy", $"{keyCount}"), Symbol: "\uE8A5"));
                if (parseError != null)
                    items.Add(new LauncherItem("파싱 오류", parseError, null, null, Symbol: "\uE783"));
                break;
            }

            case "flat":
            case "flatten":
            {
                if (parseError != null)
                {
                    items.Add(new LauncherItem("YAML 파싱 오류", parseError, null, null, Symbol: "\uE783"));
                    break;
                }
                var flat  = new List<(string Key, string Value)>();
                Flatten(yamlObj, "", flat);

                var sb = new StringBuilder();
                foreach (var (k, v) in flat) sb.AppendLine($"{k}: {v}");
                var result = sb.ToString().TrimEnd();

                items.Add(new LauncherItem($"평탄화  ({flat.Count}개 키)",
                    "Enter → 전체 복사", null, ("copy", result), Symbol: "\uE8A5"));
                foreach (var (k, v) in flat.Take(25))
                {
                    var disp = v.Length > 50 ? v[..50] + "…" : v;
                    items.Add(new LauncherItem(k, disp, null, ("copy", v), Symbol: "\uE8A5"));
                }
                if (flat.Count > 25)
                    items.Add(new LauncherItem($"… 외 {flat.Count - 25}개", "전체 복사는 첫 항목 Enter", null, null, Symbol: "\uE8A5"));
                break;
            }

            default:
            {
                items.Add(new LauncherItem("알 수 없는 서브커맨드",
                    "validate · keys · get key · stats · flat", null, null, Symbol: "\uE783"));
                break;
            }
        }

        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is ("copy", string text))
        {
            try
            {
                System.Windows.Application.Current.Dispatcher.Invoke(
                    () => Clipboard.SetText(text));
                NotificationService.Notify("YAML", "클립보드에 복사했습니다.");
            }
            catch { /* 비핵심 */ }
        }
        return Task.CompletedTask;
    }

    // ── YAML 파서 (경량, 기본 스펙만) ────────────────────────────────────────

    /// <summary>
    /// 경량 YAML 파서. 지원: 스칼라, 매핑(들여쓰기 기반), 시퀀스(- 표기).
    /// 멀티라인/앵커/태그/복잡 흐름 스타일 미지원.
    /// </summary>
    private static (object? Value, string? Error) ParseYaml(string yaml)
    {
        var error = "";
        var lines = yaml.Split('\n').Select(l => l.TrimEnd('\r')).ToList();

        // 기본 유효성: 들여쓰기 일관성 확인
        int? indentUnit = null;
        foreach (var line in lines)
        {
            if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith('#')) continue;
            var indent = line.Length - line.TrimStart().Length;
            if (indent > 0)
            {
                indentUnit ??= indent;
                // 허용 범위 내 들여쓰기인지 간단 확인
            }
        }

        // 간단 구조 파싱
        var result = ParseBlock(lines, 0, ref error, out _);
        return (result, string.IsNullOrEmpty(error) ? null : error);
    }

    private static object? ParseBlock(List<string> lines, int baseIndent, ref string error, out int consumed)
    {
        consumed = 0;
        var dict     = new Dictionary<string, object?>();
        var list     = new List<object?>();
        var isSeq    = false;
        var isMap    = false;

        var i = 0;
        while (i < lines.Count)
        {
            var line = lines[i];
            if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith('#')) { i++; continue; }

            var indent = line.Length - line.TrimStart().Length;
            if (indent < baseIndent) break;

            var trimmed = line.TrimStart();

            // 시퀀스 항목
            if (trimmed.StartsWith("- "))
            {
                isSeq = true;
                var valStr = trimmed[2..].Trim();
                if (string.IsNullOrWhiteSpace(valStr))
                {
                    // 다음 줄이 하위 블록
                    var sub = lines.Skip(i + 1).TakeWhile(l =>
                        string.IsNullOrWhiteSpace(l) || l.Length - l.TrimStart().Length > indent).ToList();
                    var innerErr = "";
                    var child = ParseBlock(sub, indent + 2, ref innerErr, out var childConsumed);
                    list.Add(child);
                    i += 1 + childConsumed;
                }
                else list.Add(ParseScalar(valStr));
                i++;
                continue;
            }

            // 매핑 항목 (key: value)
            var colonIdx = trimmed.IndexOf(':');
            if (colonIdx > 0)
            {
                isMap = true;
                var key    = trimmed[..colonIdx].Trim().Trim('"', '\'');
                var rest   = trimmed[(colonIdx + 1)..].Trim();

                if (string.IsNullOrEmpty(rest) || rest.StartsWith('#'))
                {
                    // 다음 줄이 하위 블록
                    var sub = lines.Skip(i + 1).TakeWhile(l =>
                        string.IsNullOrWhiteSpace(l) || l.Length - l.TrimStart().Length > indent).ToList();
                    var innerErr = "";
                    var child = ParseBlock(sub, indent + 2, ref innerErr, out var childConsumed);
                    dict[key] = child;
                    i += 1 + childConsumed;
                }
                else
                {
                    dict[key] = ParseScalar(rest.Split('#')[0].Trim());
                    i++;
                }
                continue;
            }

            i++;
        }

        consumed = i;
        if (isSeq) return list;
        if (isMap) return dict;
        return null;
    }

    private static object? ParseScalar(string s)
    {
        if (s is "true" or "True" or "TRUE" or "yes" or "Yes") return true;
        if (s is "false" or "False" or "FALSE" or "no" or "No") return false;
        if (s is "null" or "~" or "Null" or "NULL") return null;
        if (long.TryParse(s, out var l)) return l;
        if (double.TryParse(s, System.Globalization.NumberStyles.Any,
                System.Globalization.CultureInfo.InvariantCulture, out var d)) return d;
        return s.Trim('"', '\'');
    }

    // ── 헬퍼 ─────────────────────────────────────────────────────────────────

    private static string QuickStat(string yaml)
    {
        var lines    = yaml.Split('\n').Length;
        var keyLines = yaml.Split('\n').Count(l => KeyLineRegex().IsMatch(l));
        var depth    = GetMaxDepth(yaml);
        return $"{lines}줄  ·  키 {keyLines}개  ·  최대 깊이 {depth}단계";
    }

    private static int GetMaxDepth(string yaml)
    {
        var maxIndent = yaml.Split('\n')
            .Where(l => !string.IsNullOrWhiteSpace(l) && !l.TrimStart().StartsWith('#'))
            .Select(l => l.Length - l.TrimStart().Length)
            .DefaultIfEmpty(0).Max();
        return maxIndent / 2 + 1;
    }

    private static int CountKeys(object? node) => node switch
    {
        Dictionary<string, object?> d => d.Count + d.Values.Sum(v => CountKeys(v)),
        List<object?> l               => l.Sum(v => CountKeys(v)),
        _                             => 0,
    };

    private static object? GetByPath(object? node, string[] parts)
    {
        if (parts.Length == 0) return node;
        if (node is Dictionary<string, object?> dict)
        {
            var key = parts[0];
            if (!dict.TryGetValue(key, out var child)) return null;
            return GetByPath(child, parts[1..]);
        }
        if (node is List<object?> list && int.TryParse(parts[0], out var idx) && idx < list.Count)
            return GetByPath(list[idx], parts[1..]);
        return null;
    }

    private static void Flatten(object? node, string prefix, List<(string, string)> result)
    {
        switch (node)
        {
            case Dictionary<string, object?> dict:
                foreach (var (k, v) in dict)
                    Flatten(v, string.IsNullOrEmpty(prefix) ? k : $"{prefix}.{k}", result);
                break;
            case List<object?> list:
                for (var i = 0; i < list.Count; i++)
                    Flatten(list[i], $"{prefix}[{i}]", result);
                break;
            default:
                result.Add((prefix, FormatValue(node)));
                break;
        }
    }

    private static string FormatValue(object? v) => v switch
    {
        null                          => "(null)",
        bool b                        => b ? "true" : "false",
        Dictionary<string, object?> d => $"{{...{d.Count}개 키}}",
        List<object?> l               => $"[...{l.Count}개 항목]",
        _                             => v.ToString() ?? "",
    };

    [GeneratedRegex(@"^\s*[\w\-""']+\s*:")]
    private static partial Regex KeyLineRegex();
}
