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

namespace AxCopilot.Handlers;

/// <summary>
/// L17-2: 숫자 포맷·읽기 변환 핸들러. "num" 프리픽스로 사용합니다.
///
/// 예: num 1234567             → 천단위·한글·영어·진수·과학표기 모두 표시
///     num 0xff                → 16진수 → 10진수 변환
///     num 0b1010              → 2진수 → 10진수 변환
///     num 42 ko               → 한국어로 읽기 (사십이)
///     num 42 en               → 영어로 읽기 (forty-two)
///     num 1e6                 → 과학표기 → 일반 변환
/// Enter → 결과 복사.
/// </summary>
public class NumHandler : IActionHandler
{
    public string? Prefix => "num";

    public PluginMetadata Metadata => new(
        "Num",
        "숫자 포맷·읽기 변환 — 한글·영어·진수·천단위·과학표기",
        "1.0",
        "AX");

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

        if (string.IsNullOrWhiteSpace(q))
        {
            items.Add(new LauncherItem("숫자 포맷·읽기 변환기",
                "예: num 1234567  /  num 0xff  /  num 42 ko  /  num 42 en",
                null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("num <숫자>",     "천단위·한글·영어·진수 변환", null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("num 0xff",       "16진수 입력",               null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("num 0b1010",     "2진수 입력",                null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("num 0o17",       "8진수 입력",                null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("num 42 ko",      "한국어로 읽기",             null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("num 42 en",      "영어로 읽기",               null, null, Symbol: "\uE8EF"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var parts = q.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        var numStr = parts[0];
        var mode   = parts.Length > 1 ? parts[1].ToLowerInvariant() : null;

        // 숫자 파싱 (여러 진수 지원)
        if (!TryParseNumber(numStr, out var value, out var inputBase))
        {
            items.Add(new LauncherItem("숫자 형식 오류",
                "정수 또는 0x/0b/0o 접두사 숫자를 입력하세요", null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 한국어 읽기 전용
        if (mode is "ko" or "kr" or "한국어" or "한글")
        {
            var ko = ToKorean(value);
            items.Add(new LauncherItem(ko, $"{value:N0} 한국어 읽기", null, ("copy", ko), Symbol: "\uE8EF"));
            var ko2 = ToKoreanMoney(value);
            items.Add(new LauncherItem(ko2, "금액 읽기 (원)", null, ("copy", ko2), Symbol: "\uE8EF"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 영어 읽기 전용
        if (mode is "en" or "english" or "영어")
        {
            var en = ToEnglish(value);
            items.Add(new LauncherItem(en, $"{value:N0} 영어 읽기", null, ("copy", en), Symbol: "\uE8EF"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 전체 변환 표시
        items.Add(new LauncherItem($"{value:N0}",
            $"입력값 ({inputBase}진수 → 10진수)",
            null, ("copy", $"{value}"), Symbol: "\uE8EF"));

        // 천단위 구분
        var commaSep = $"{value:N0}";
        items.Add(new LauncherItem(commaSep, "천단위 구분", null, ("copy", commaSep), Symbol: "\uE8EF"));

        // 한글 단위 (만·억·조)
        var krUnit = ToKoreanUnit(value);
        items.Add(new LauncherItem(krUnit, "한글 단위", null, ("copy", krUnit), Symbol: "\uE8EF"));

        // 한국어 읽기
        var koRead = ToKorean(value);
        items.Add(new LauncherItem(koRead, "한국어 읽기", null, ("copy", koRead), Symbol: "\uE8EF"));

        // 영어 읽기
        if (Math.Abs(value) < 1_000_000_000_000L)
        {
            var enRead = ToEnglish(value);
            items.Add(new LauncherItem(enRead, "영어 읽기", null, ("copy", enRead), Symbol: "\uE8EF"));
        }

        // 진수 변환 (정수 범위만)
        if (value >= 0 && value <= long.MaxValue)
        {
            var lv = (long)value;
            var hex = $"0x{lv:X}";
            var bin = Convert.ToString(lv, 2);
            var oct = Convert.ToString(lv, 8);
            items.Add(new LauncherItem($"── 진수 변환 ──", "", null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem($"16진수  {hex}",   $"Hex", null, ("copy", hex), Symbol: "\uE8EF"));
            items.Add(new LauncherItem($"8진수   0o{oct}", $"Octal", null, ("copy", $"0o{oct}"), Symbol: "\uE8EF"));
            items.Add(new LauncherItem($"2진수   {bin}",   $"Binary ({bin.Length}bit)", null, ("copy", bin), Symbol: "\uE8EF"));
        }

        // 과학 표기
        var sci = $"{value:E4}";
        items.Add(new LauncherItem($"과학 표기  {sci}", "Scientific Notation", null, ("copy", sci), Symbol: "\uE8EF"));

        // 로마 숫자 (1~3999)
        if (value >= 1 && value <= 3999)
        {
            var roman = ToRoman((int)value);
            items.Add(new LauncherItem($"로마 숫자  {roman}", "Roman Numerals", null, ("copy", roman), Symbol: "\uE8EF"));
        }

        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("Num", "클립보드에 복사했습니다.");
            }
            catch { /* 비핵심 */ }
        }
        return Task.CompletedTask;
    }

    // ── 파싱 ─────────────────────────────────────────────────────────────────

    private static bool TryParseNumber(string s, out double value, out int inputBase)
    {
        value     = 0;
        inputBase = 10;
        s = s.Replace(",", "").Trim();

        if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ||
            s.StartsWith("&H", StringComparison.OrdinalIgnoreCase))
        {
            inputBase = 16;
            var hex = s[2..];
            if (long.TryParse(hex, System.Globalization.NumberStyles.HexNumber,
                    System.Globalization.CultureInfo.InvariantCulture, out var lv))
            { value = lv; return true; }
            return false;
        }
        if (s.StartsWith("0b", StringComparison.OrdinalIgnoreCase))
        {
            inputBase = 2;
            try { value = System.Convert.ToInt64(s[2..], 2); return true; }
            catch { return false; }
        }
        if (s.StartsWith("0o", StringComparison.OrdinalIgnoreCase))
        {
            inputBase = 8;
            try { value = System.Convert.ToInt64(s[2..], 8); return true; }
            catch { return false; }
        }

        // 과학 표기법 포함 일반 double
        if (double.TryParse(s, System.Globalization.NumberStyles.Any,
                System.Globalization.CultureInfo.InvariantCulture, out value))
            return true;

        return false;
    }

    // ── 한글 단위 ─────────────────────────────────────────────────────────────

    private static string ToKoreanUnit(double value)
    {
        var abs = Math.Abs(value);
        var sign = value < 0 ? "-" : "";

        if (abs >= 1_0000_0000_0000) return $"{sign}{abs / 1_0000_0000_0000:N2}조";
        if (abs >= 1_0000_0000)      return $"{sign}{abs / 1_0000_0000:N2}억";
        if (abs >= 1_0000)           return $"{sign}{abs / 1_0000:N2}만";
        return $"{sign}{abs:N0}";
    }

    private static readonly string[] KoOnes    = ["", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구"];
    private static readonly string[] KoTens    = ["", "십", "이십", "삼십", "사십", "오십", "육십", "칠십", "팔십", "구십"];
    private static readonly string[] KoHundreds= ["", "백", "이백", "삼백", "사백", "오백", "육백", "칠백", "팔백", "구백"];

    private static string ToKorean(double value)
    {
        if (value == 0) return "영";
        var abs  = (long)Math.Abs(value);
        var sign = value < 0 ? "마이너스 " : "";
        return sign + KoNumber(abs);
    }

    private static string KoNumber(long n)
    {
        if (n == 0) return "";
        var sb   = new StringBuilder();
        var jo   = n / 1_0000_0000_0000L;
        var eok  = n % 1_0000_0000_0000L / 1_0000_0000L;
        var man  = n % 1_0000_0000L / 1_0000L;
        var rest = n % 1_0000L;

        if (jo  > 0) { sb.Append(KoUnder1만(jo));  sb.Append("조 "); }
        if (eok > 0) { sb.Append(KoUnder1만(eok)); sb.Append("억 "); }
        if (man > 0) { sb.Append(KoUnder1만(man)); sb.Append("만 "); }
        if (rest> 0) { sb.Append(KoUnder1만(rest)); }
        return sb.ToString().Trim();
    }

    private static string KoUnder1만(long n)
    {
        var sb   = new StringBuilder();
        var thou = (int)(n / 1000);
        var hund = (int)(n % 1000 / 100);
        var ten  = (int)(n % 100 / 10);
        var one  = (int)(n % 10);
        if (thou > 0) { sb.Append(thou == 1 ? "천" : KoOnes[thou] + "천"); }
        if (hund > 0) { sb.Append(hund == 1 ? "백" : KoOnes[hund] + "백"); }
        if (ten  > 0) { sb.Append(ten  == 1 ? "십" : KoOnes[ten]  + "십"); }
        if (one  > 0) { sb.Append(KoOnes[one]); }
        return sb.ToString();
    }

    private static string ToKoreanMoney(double value)
    {
        var ko = ToKorean(value);
        return string.IsNullOrEmpty(ko) ? "영 원" : $"{ko} 원";
    }

    // ── 영어 읽기 ─────────────────────────────────────────────────────────────

    private static readonly string[] EnOnes =
        ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
         "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
         "sixteen", "seventeen", "eighteen", "nineteen"];
    private static readonly string[] EnTens =
        ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];

    private static string ToEnglish(double value)
    {
        if (value == 0) return "zero";
        var n    = (long)Math.Abs(value);
        var sign = value < 0 ? "negative " : "";
        return sign + EnNumber(n);
    }

    private static string EnNumber(long n)
    {
        if (n == 0) return "";
        if (n < 20)  return EnOnes[n];
        if (n < 100) return EnTens[n / 10] + (n % 10 > 0 ? "-" + EnOnes[n % 10] : "");
        if (n < 1000)
        {
            var rest = n % 100 > 0 ? " and " + EnNumber(n % 100) : "";
            return EnOnes[n / 100] + " hundred" + rest;
        }
        if (n < 1_000_000)     return EnNumber(n / 1000) + " thousand" + (n % 1000 > 0 ? " " + EnNumber(n % 1000) : "");
        if (n < 1_000_000_000) return EnNumber(n / 1_000_000) + " million" + (n % 1_000_000 > 0 ? " " + EnNumber(n % 1_000_000) : "");
        return EnNumber(n / 1_000_000_000) + " billion" + (n % 1_000_000_000 > 0 ? " " + EnNumber(n % 1_000_000_000) : "");
    }

    // ── 로마 숫자 ─────────────────────────────────────────────────────────────

    private static string ToRoman(int n)
    {
        var vals = new[] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
        var syms = new[] { "M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I" };
        var sb   = new StringBuilder();
        for (var i = 0; i < vals.Length; i++)
            while (n >= vals[i]) { sb.Append(syms[i]); n -= vals[i]; }
        return sb.ToString();
    }
}
