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

namespace AxCopilot.Handlers;

/// <summary>
/// L21-1: TOML 파서·분석기 핸들러. "toml" 프리픽스로 사용합니다.
///
/// 예: toml                   → 클립보드 TOML 전체 키 목록
///     toml validate          → 유효성 검사
///     toml keys              → 최상위 키 목록
///     toml get key           → 특정 키 값 조회
///     toml get server.port   → 점 표기법 중첩 키 조회
///     toml stats             → 줄·키·섹션·배열 통계
///     toml flat              → 점 표기법 평탄화 (모든 키·값)
///     toml sections          → [section] 목록
/// Enter → 값 복사.
/// </summary>
public partial class TomlHandler : IActionHandler
{
    public string? Prefix => "toml";

    public PluginMetadata Metadata => new(
        "TOML",
        "TOML 파서·분석기 — 키 조회·유효성 검사·평탄화",
        "1.0",
        "AX");

    // TOML 노드 (경량 표현)
    private sealed class TomlTable : Dictionary<string, object?> { }
    private sealed class TomlArray  : List<object?> { }

    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().Trim();
            });
        }
        catch { }

        if (string.IsNullOrWhiteSpace(q))
        {
            if (string.IsNullOrWhiteSpace(clipboard))
            {
                items.Add(new LauncherItem("TOML 파서·분석기",
                    "클립보드에 TOML을 복사하세요 · toml validate / keys / get / stats / flat",
                    null, null, Symbol: "\uE8EC"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }
            // 기본: 유효성 확인 + 최상위 키
            var (tbl, err) = ParseToml(clipboard!);
            if (err != null)
            {
                items.Add(ErrorItem($"TOML 파싱 오류: {err}"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }
            items.Add(new LauncherItem("TOML 파싱 성공 ✓",
                $"최상위 키 {tbl!.Count}개  · toml get / flat / stats", null, null, Symbol: "\uE8EC"));
            BuildTopKeys(items, tbl!);
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        var src = parts.Length > 1 && (sub != "get")
            ? string.Join(" ", parts[1..])
            : clipboard ?? "";

        // validate
        if (sub is "validate" or "check" or "검사")
        {
            var text = clipboard ?? "";
            var (_, verr) = ParseToml(text);
            if (verr != null)
                items.Add(ErrorItem($"유효성 오류: {verr}"));
            else
            {
                var (vt, _) = ParseToml(text);
                items.Add(new LauncherItem("✓ 유효한 TOML", $"최상위 키 {vt!.Count}개", null, null, Symbol: "\uE8EC"));
                BuildTopKeys(items, vt!);
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var (table, parseErr) = ParseToml(clipboard ?? "");
        if (parseErr != null && sub != "validate")
        {
            items.Add(ErrorItem($"TOML 파싱 오류: {parseErr}"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        switch (sub)
        {
            case "keys" or "key":
                items.Add(new LauncherItem("최상위 키 목록", $"{table!.Count}개", null, null, Symbol: "\uE8EC"));
                BuildTopKeys(items, table!);
                break;

            case "sections" or "section":
            {
                var secs = table!.Where(kv => kv.Value is TomlTable).Select(kv => kv.Key).ToList();
                items.Add(new LauncherItem($"섹션 {secs.Count}개", "", null, null, Symbol: "\uE8EC"));
                foreach (var s in secs)
                {
                    var secCount = table![s] is TomlTable st ? st.Count : 0;
                    items.Add(new LauncherItem($"[{s}]",
                        $"{secCount}개 키", null, ("copy", s), Symbol: "\uE8EC"));
                }
                break;
            }

            case "get":
            {
                if (parts.Length < 2) { items.Add(ErrorItem("예: toml get server.port")); break; }
                var keyPath = parts[1];
                var val = GetByPath(table!, keyPath);
                if (val == null)
                    items.Add(new LauncherItem($"'{keyPath}' 키를 찾을 수 없습니다", "", null, null, Symbol: "\uE8EC"));
                else
                {
                    var strVal = TomlValueToString(val);
                    items.Add(new LauncherItem($"{keyPath} = {TruncateStr(strVal, 60)}",
                        "Enter 복사", null, ("copy", strVal), Symbol: "\uE8EC"));
                    items.Add(CopyItem("값",       strVal));
                    items.Add(CopyItem("키 경로",  keyPath));
                    items.Add(CopyItem("타입",     GetTomlType(val)));
                }
                break;
            }

            case "stats":
            {
                var flat = new Dictionary<string, object?>();
                FlattenTable(table!, "", flat);
                var lines   = (clipboard ?? "").Split('\n').Length;
                var arrays  = flat.Values.Count(v => v is TomlArray);
                var tables2 = flat.Values.Count(v => v is TomlTable);
                var scalars = flat.Count - arrays - tables2;

                items.Add(new LauncherItem("TOML 통계", "", null, null, Symbol: "\uE8EC"));
                items.Add(CopyItem("전체 줄",      lines.ToString()));
                items.Add(CopyItem("최상위 키",    table!.Count.ToString()));
                items.Add(CopyItem("전체 키(flat)", flat.Count.ToString()));
                items.Add(CopyItem("스칼라 값",    scalars.ToString()));
                items.Add(CopyItem("배열",          arrays.ToString()));
                items.Add(CopyItem("섹션(테이블)", table.Values.Count(v => v is TomlTable).ToString()));
                break;
            }

            case "flat" or "flatten":
            {
                var flat = new Dictionary<string, object?>();
                FlattenTable(table!, "", flat);
                var all = string.Join("\n", flat.Select(kv => $"{kv.Key} = {TomlValueToString(kv.Value)}"));
                items.Add(new LauncherItem($"평탄화 결과 {flat.Count}개",
                    "전체 복사 → Enter", null, ("copy", all), Symbol: "\uE8EC"));
                foreach (var (k, v) in flat.Take(20))
                    items.Add(new LauncherItem($"{k} = {TruncateStr(TomlValueToString(v), 50)}",
                        GetTomlType(v), null, ("copy", TomlValueToString(v)), Symbol: "\uE8EC"));
                if (flat.Count > 20)
                    items.Add(new LauncherItem($"... ({flat.Count - 20}개 더)", "전체는 Enter로 복사",
                        null, null, Symbol: "\uE8EC"));
                break;
            }

            default:
                items.Add(new LauncherItem($"알 수 없는 서브커맨드: '{sub}'",
                    "validate · keys · sections · 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("TOML", "클립보드에 복사했습니다.");
            }
            catch { }
        }
        return Task.CompletedTask;
    }

    // ── 경량 TOML 파서 ────────────────────────────────────────────────────

    private static (TomlTable? table, string? error) ParseToml(string src)
    {
        var root    = new TomlTable();
        var current = root;
        var lines   = src.Split('\n');
        string? parseError = null;

        for (int lineNo = 0; lineNo < lines.Length; lineNo++)
        {
            var raw  = lines[lineNo];
            var line = StripComment(raw).Trim();
            if (string.IsNullOrWhiteSpace(line)) continue;

            // [section] 또는 [[array-of-tables]]
            if (line.StartsWith("[["))
            {
                var name = line.Trim('[', ']').Trim();
                // 배열 섹션 처리 (간략화: 마지막 테이블만 유지)
                var parts  = name.Split('.');
                current    = root;
                foreach (var p in parts)
                {
                    if (!current.TryGetValue(p, out var existing) || existing is not TomlTable child)
                    { child = new TomlTable(); current[p] = child; }
                    current = (TomlTable)current[p]!;
                }
            }
            else if (line.StartsWith("["))
            {
                var name  = line.Trim('[', ']').Trim();
                var parts = name.Split('.');
                current   = root;
                foreach (var p in parts)
                {
                    if (!current.TryGetValue(p, out var existing) || existing is not TomlTable child)
                    { child = new TomlTable(); current[p] = child; }
                    current = (TomlTable)current[p]!;
                }
            }
            // key = value
            else if (line.Contains('='))
            {
                var eqIdx = line.IndexOf('=');
                var key   = line[..eqIdx].Trim().Trim('"');
                var val   = line[(eqIdx + 1)..].Trim();
                try { current[key] = ParseTomlValue(val); }
                catch (Exception ex)
                { parseError ??= $"줄 {lineNo + 1}: {ex.Message}"; }
            }
        }
        return parseError != null ? (null, parseError) : (root, null);
    }

    private static object? ParseTomlValue(string val)
    {
        if (val.StartsWith('"') || val.StartsWith('\''))
            return val.Trim('"', '\'');
        if (val.StartsWith('['))
        {
            var arr = new TomlArray();
            var inner = val.Trim('[', ']');
            foreach (var item in inner.Split(','))
            {
                var t = item.Trim();
                if (!string.IsNullOrEmpty(t)) arr.Add(ParseTomlValue(t));
            }
            return arr;
        }
        if (val.StartsWith('{'))
        {
            var tbl = new TomlTable();
            var inner = val.Trim('{', '}');
            foreach (var pair in inner.Split(','))
            {
                var parts = pair.Split('=', 2);
                if (parts.Length == 2)
                    tbl[parts[0].Trim().Trim('"')] = ParseTomlValue(parts[1].Trim());
            }
            return tbl;
        }
        if (val is "true")  return true;
        if (val is "false") return false;
        if (long.TryParse(val, out var lv))   return lv;
        if (double.TryParse(val, System.Globalization.NumberStyles.Float,
            System.Globalization.CultureInfo.InvariantCulture, out var dv)) return dv;
        return val.Trim('"', '\'');
    }

    private static string StripComment(string line)
    {
        // '#' 앞에 따옴표가 홀수개인 경우 내부 → 유지, 아니면 제거
        bool inStr = false;
        char quote = '"';
        for (int i = 0; i < line.Length; i++)
        {
            var c = line[i];
            if (!inStr && (c == '"' || c == '\'')) { inStr = true; quote = c; }
            else if (inStr && c == quote) inStr = false;
            else if (!inStr && c == '#') return line[..i];
        }
        return line;
    }

    private static object? GetByPath(TomlTable table, string path)
    {
        var parts = path.Split('.');
        object? cur = table;
        foreach (var p in parts)
        {
            if (cur is TomlTable t && t.TryGetValue(p, out var next)) cur = next;
            else return null;
        }
        return cur;
    }

    private static void FlattenTable(TomlTable table, string prefix, Dictionary<string, object?> result)
    {
        foreach (var (k, v) in table)
        {
            var key = string.IsNullOrEmpty(prefix) ? k : $"{prefix}.{k}";
            if (v is TomlTable child) FlattenTable(child, key, result);
            else result[key] = v;
        }
    }

    private static void BuildTopKeys(List<LauncherItem> items, TomlTable table)
    {
        foreach (var (k, v) in table)
        {
            var type  = GetTomlType(v);
            var disp  = v is TomlTable t ? $"{{ {t.Count}개 키 }}" :
                        v is TomlArray  a ? $"[ {a.Count}개 항목 ]" :
                        TruncateStr(TomlValueToString(v), 50);
            items.Add(new LauncherItem($"{k} = {disp}", type,
                null, ("copy", TomlValueToString(v)), Symbol: "\uE8EC"));
        }
    }

    private static string TomlValueToString(object? v) => v switch
    {
        null         => "null",
        bool b       => b ? "true" : "false",
        TomlTable t  => "{" + string.Join(", ", t.Select(kv => $"{kv.Key} = {TomlValueToString(kv.Value)}")) + "}",
        TomlArray  a => "[" + string.Join(", ", a.Select(TomlValueToString)) + "]",
        _            => v.ToString() ?? ""
    };

    private static string GetTomlType(object? v) => v switch
    {
        null       => "null",
        bool       => "Boolean",
        long       => "Integer",
        double     => "Float",
        string     => "String",
        TomlTable  => "Table",
        TomlArray  => "Array",
        _          => v.GetType().Name
    };

    private static string TruncateStr(string s, int max) =>
        s.Length <= max ? s : s[..max] + "…";

    private static LauncherItem CopyItem(string label, string value) =>
        new(label, value, null, ("copy", value), Symbol: "\uE8EC");

    private static LauncherItem ErrorItem(string msg) =>
        new(msg, "올바른 입력 형식을 확인하세요", null, null, Symbol: "\uE783");
}
