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

namespace AxCopilot.Handlers;

/// <summary>
/// L15-2: 환율 변환기 핸들러. "currency" 프리픽스로 사용합니다.
///
/// 예: currency                → 주요 통화 기준환율 목록
///     currency 100 usd        → 100 USD → KRW
///     currency 100 usd eur    → 100 USD → EUR
///     currency 50000 krw usd  → 50,000 KRW → USD
///     currency rates          → 전체 환율표
/// Enter → 결과를 클립보드에 복사.
///
/// 내장 기준환율 사용 (사내 모드). 사외 모드에서는 동일하게 동작.
/// </summary>
public class CurrencyHandler : IActionHandler
{
    public string? Prefix => "currency";

    public PluginMetadata Metadata => new(
        "Currency",
        "환율 변환기 — KRW·USD·EUR·JPY·CNY 등 주요 통화 변환",
        "1.0",
        "AX");

    // 내장 기준환율 (KRW 기준, 2025년 1분기 평균 참조값)
    private static readonly Dictionary<string, (string Name, string Symbol, double RateToKrw)> Rates =
        new(StringComparer.OrdinalIgnoreCase)
        {
            ["KRW"] = ("한국 원",         "₩",  1.0),
            ["USD"] = ("미국 달러",        "$",  1370.0),
            ["EUR"] = ("유로",             "€",  1480.0),
            ["JPY"] = ("일본 엔",          "¥",  9.2),
            ["CNY"] = ("중국 위안",        "¥",  189.0),
            ["GBP"] = ("영국 파운드",      "£",  1730.0),
            ["HKD"] = ("홍콩 달러",        "HK$",175.0),
            ["TWD"] = ("대만 달러",        "NT$", 42.0),
            ["SGD"] = ("싱가포르 달러",    "S$", 1020.0),
            ["AUD"] = ("호주 달러",        "A$",  870.0),
            ["CAD"] = ("캐나다 달러",      "C$",  995.0),
            ["CHF"] = ("스위스 프랑",      "Fr",  1540.0),
            ["MYR"] = ("말레이시아 링깃",  "RM",  310.0),
            ["THB"] = ("태국 바트",        "฿",   38.5),
            ["VND"] = ("베트남 동",        "₫",   0.054),
        };

    // 통화 별칭
    private static readonly Dictionary<string, string> Aliases =
        new(StringComparer.OrdinalIgnoreCase)
        {
            ["달러"] = "USD", ["엔"] = "JPY", ["위안"] = "CNY", ["유로"] = "EUR",
            ["파운드"] = "GBP", ["원"] = "KRW", ["엔화"] = "JPY", ["달러화"] = "USD",
            ["프랑"] = "CHF", ["바트"] = "THB", ["동"] = "VND", ["링깃"] = "MYR",
        };

    // 주요 통화 표시 순서
    private static readonly string[] MainCurrencies = ["USD", "EUR", "JPY", "CNY", "GBP", "HKD", "SGD", "AUD"];

    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("환율 변환기",
                "예: currency 100 usd  /  currency 50000 krw eur  /  currency rates",
                null, null, Symbol: "\uE8C7"));
            items.Add(new LauncherItem("── 주요 통화 (KRW 기준) ──", "", null, null, Symbol: "\uE8C7"));
            foreach (var code in MainCurrencies)
            {
                if (!Rates.TryGetValue(code, out var info)) continue;
                items.Add(new LauncherItem(
                    $"1 {code}  =  {info.RateToKrw:N0} KRW",
                    $"{info.Name}  ({info.Symbol})",
                    null, ("copy", $"1 {code} = {info.RateToKrw:N0} KRW"),
                    Symbol: "\uE8C7"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var parts = q.Split(' ', StringSplitOptions.RemoveEmptyEntries);

        // rates → 전체 환율표
        if (parts[0].Equals("rates", StringComparison.OrdinalIgnoreCase) ||
            parts[0].Equals("list", StringComparison.OrdinalIgnoreCase))
        {
            items.Add(new LauncherItem($"전체 환율표  ({Rates.Count}개 통화)", "KRW 기준 내장 환율",
                null, null, Symbol: "\uE8C7"));
            foreach (var (code, info) in Rates.OrderBy(r => r.Key))
            {
                items.Add(new LauncherItem(
                    $"1 {code}  =  {info.RateToKrw:N2} KRW",
                    $"{info.Symbol}  {info.Name}",
                    null, ("copy", $"1 {code} = {info.RateToKrw:N2} KRW"),
                    Symbol: "\uE8C7"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 금액 파싱
        if (!TryParseAmount(parts[0], out var amount))
        {
            items.Add(new LauncherItem("금액 형식 오류",
                "예: currency 100 usd", null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 통화 코드 파싱
        var fromCode = parts.Length >= 2 ? ResolveCode(parts[1]) : "KRW";
        var toCode   = parts.Length >= 3 ? ResolveCode(parts[2]) : null;

        if (fromCode == null)
        {
            items.Add(new LauncherItem("알 수 없는 통화",
                $"'{parts[1]}' 코드를 찾을 수 없습니다. 예: USD EUR JPY CNY",
                null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        if (!Rates.TryGetValue(fromCode, out var fromInfo))
        {
            items.Add(new LauncherItem("지원하지 않는 통화", fromCode, null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var amountKrw = amount * fromInfo.RateToKrw;

        // 특정 대상 통화 지정
        if (toCode != null)
        {
            if (!Rates.TryGetValue(toCode, out var toInfo))
            {
                items.Add(new LauncherItem("지원하지 않는 대상 통화", toCode, null, null, Symbol: "\uE783"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }

            var converted = amountKrw / toInfo.RateToKrw;
            var label     = $"{FormatAmount(amount, fromCode)}  =  {FormatAmount(converted, toCode)}";
            items.Add(new LauncherItem(label,
                $"{fromInfo.Name} → {toInfo.Name}  (Enter 복사)",
                null, ("copy", label), Symbol: "\uE8C7"));
            items.Add(new LauncherItem($"{FormatAmount(converted, toCode)}", $"변환 결과",
                null, ("copy", $"{converted:N2}"), Symbol: "\uE8C7"));
        }
        else
        {
            // 주요 통화들로 일괄 변환
            items.Add(new LauncherItem(
                $"{FormatAmount(amount, fromCode)}  변환 결과",
                "주요 통화 기준 (내장 환율)",
                null, null, Symbol: "\uE8C7"));

            var targets = fromCode == "KRW" ? MainCurrencies : (new[] { "KRW" }).Concat(MainCurrencies.Where(c => c != fromCode)).ToArray();
            foreach (var tc in targets)
            {
                if (!Rates.TryGetValue(tc, out var tInfo)) continue;
                var conv  = amountKrw / tInfo.RateToKrw;
                var label = $"{FormatAmount(conv, tc)}";
                items.Add(new LauncherItem(label,
                    $"{tInfo.Name}  ({tInfo.Symbol})",
                    null, ("copy", label), Symbol: "\uE8C7"));
            }
        }

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

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

    private static string? ResolveCode(string input)
    {
        if (Rates.ContainsKey(input)) return input.ToUpperInvariant();
        if (Aliases.TryGetValue(input, out var code)) return code;
        return null;
    }

    private static bool TryParseAmount(string s, out double result)
    {
        result = 0;
        s = s.Replace(",", "").Trim();
        return double.TryParse(s, System.Globalization.NumberStyles.Any,
            System.Globalization.CultureInfo.InvariantCulture, out result);
    }

    private static string FormatAmount(double amount, string code)
    {
        if (!Rates.TryGetValue(code, out var info)) return $"{amount:N2} {code}";
        // 소수점 자릿수: JPY/KRW/VND = 0, 기타 = 2
        var decimals = code is "JPY" or "KRW" or "VND" ? 0 : 2;
        var fmt = decimals == 0 ? $"{amount:N0}" : $"{amount:N2}";
        return $"{info.Symbol}{fmt} {code}";
    }
}
