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

namespace AxCopilot.Handlers;

/// <summary>
/// L17-1: 단위 변환기 핸들러. "unit" 프리픽스로 사용합니다.
///
/// 예: unit 100 km m           → 100km → m
///     unit 72 f c             → 화씨 72°F → 섭씨
///     unit 5 kg lb            → 5kg → 파운드
///     unit 1 gb mb            → 1GB → MB
///     unit 60 mph kmh         → 속도 변환
///     unit length             → 길이 단위 목록
///     unit weight / temp / area / speed / data → 카테고리 목록
/// Enter → 결과 복사.
/// </summary>
public class UnitHandler : IActionHandler
{
    public string? Prefix => "unit";

    public PluginMetadata Metadata => new(
        "Unit",
        "단위 변환기 — 길이·무게·온도·넓이·속도·데이터",
        "1.0",
        "AX");

    // ── 단위 정의 (기준 단위 → SI 변환 계수) ─────────────────────────────────
    // 온도는 별도 처리 (비선형)

    private enum UnitCategory { Length, Weight, Area, Speed, Data, Temperature, Pressure, Volume }

    private record UnitDef(string[] Names, double ToBase, UnitCategory Cat, string Display);

    private static readonly UnitDef[] Units =
    [
        // 길이 (기준: m)
        new(["km","킬로미터"],             1000,         UnitCategory.Length,      "킬로미터 (km)"),
        new(["m","미터"],                  1,            UnitCategory.Length,      "미터 (m)"),
        new(["cm","센티미터"],             0.01,         UnitCategory.Length,      "센티미터 (cm)"),
        new(["mm","밀리미터"],             0.001,        UnitCategory.Length,      "밀리미터 (mm)"),
        new(["mi","mile","마일"],          1609.344,     UnitCategory.Length,      "마일 (mi)"),
        new(["yd","yard","야드"],          0.9144,       UnitCategory.Length,      "야드 (yd)"),
        new(["ft","feet","foot","피트"],   0.3048,       UnitCategory.Length,      "피트 (ft)"),
        new(["in","inch","인치"],          0.0254,       UnitCategory.Length,      "인치 (in)"),
        new(["nm","해리"],                 1852,         UnitCategory.Length,      "해리 (nm)"),

        // 무게 (기준: kg)
        new(["t","ton","톤"],             1000,          UnitCategory.Weight,      "톤 (t)"),
        new(["kg","킬로그램"],             1,            UnitCategory.Weight,      "킬로그램 (kg)"),
        new(["g","그램"],                  0.001,        UnitCategory.Weight,      "그램 (g)"),
        new(["mg","밀리그램"],             1e-6,         UnitCategory.Weight,      "밀리그램 (mg)"),
        new(["lb","lbs","파운드"],         0.453592,     UnitCategory.Weight,      "파운드 (lb)"),
        new(["oz","온스"],                 0.0283495,    UnitCategory.Weight,      "온스 (oz)"),
        new(["근"],                        0.6,          UnitCategory.Weight,      "근 (600g)"),

        // 넓이 (기준: m²)
        new(["km2","km²"],                 1e6,          UnitCategory.Area,        "제곱킬로미터 (km²)"),
        new(["m2","m²","sqm"],             1,            UnitCategory.Area,        "제곱미터 (m²)"),
        new(["cm2","cm²"],                 0.0001,       UnitCategory.Area,        "제곱센티미터 (cm²)"),
        new(["ha","헥타르"],               10000,        UnitCategory.Area,        "헥타르 (ha)"),
        new(["a","아르"],                  100,          UnitCategory.Area,        "아르 (a)"),
        new(["acre","에이커"],             4046.856,     UnitCategory.Area,        "에이커 (acre)"),
        new(["ft2","ft²","sqft"],          0.092903,     UnitCategory.Area,        "제곱피트 (ft²)"),
        new(["평"],                        3.30579,      UnitCategory.Area,        "평 (3.3058m²)"),

        // 속도 (기준: m/s)
        new(["mps","m/s"],                 1,            UnitCategory.Speed,       "미터/초 (m/s)"),
        new(["kph","kmh","km/h","kmph"],   0.277778,     UnitCategory.Speed,       "킬로미터/시 (km/h)"),
        new(["mph","mi/h"],                0.44704,      UnitCategory.Speed,       "마일/시 (mph)"),
        new(["knot","kn","노트"],          0.514444,     UnitCategory.Speed,       "노트 (kn)"),
        new(["fps","ft/s"],                0.3048,       UnitCategory.Speed,       "피트/초 (ft/s)"),

        // 데이터 (기준: byte)
        new(["b","bit","비트"],            0.125,        UnitCategory.Data,        "비트 (bit)"),
        new(["byte","바이트"],             1,            UnitCategory.Data,        "바이트 (byte)"),
        new(["kb","킬로바이트"],           1024,         UnitCategory.Data,        "킬로바이트 (KB)"),
        new(["mb","메가바이트"],           1048576,      UnitCategory.Data,        "메가바이트 (MB)"),
        new(["gb","기가바이트"],           1073741824,   UnitCategory.Data,        "기가바이트 (GB)"),
        new(["tb","테라바이트"],           1099511627776,UnitCategory.Data,        "테라바이트 (TB)"),
        new(["pb","페타바이트"],           1.12589990684e15, UnitCategory.Data,    "페타바이트 (PB)"),

        // 온도 (기준: °C, 변환은 특수 처리)
        new(["c","°c","celsius","섭씨"],   1,            UnitCategory.Temperature, "섭씨 (°C)"),
        new(["f","°f","fahrenheit","화씨"],1,            UnitCategory.Temperature, "화씨 (°F)"),
        new(["k","kelvin","켈빈"],         1,            UnitCategory.Temperature, "켈빈 (K)"),

        // 압력 (기준: Pa)
        new(["pa","파스칼"],               1,            UnitCategory.Pressure,    "파스칼 (Pa)"),
        new(["kpa"],                       1000,         UnitCategory.Pressure,    "킬로파스칼 (kPa)"),
        new(["mpa"],                       1e6,          UnitCategory.Pressure,    "메가파스칼 (MPa)"),
        new(["atm","기압"],                101325,       UnitCategory.Pressure,    "기압 (atm)"),
        new(["bar","바"],                  100000,       UnitCategory.Pressure,    "바 (bar)"),
        new(["psi"],                       6894.757,     UnitCategory.Pressure,    "PSI (psi)"),

        // 부피 (기준: L)
        new(["l","liter","리터"],          1,            UnitCategory.Volume,      "리터 (L)"),
        new(["ml","밀리리터"],             0.001,        UnitCategory.Volume,      "밀리리터 (mL)"),
        new(["m3","m³","cbm"],             1000,         UnitCategory.Volume,      "세제곱미터 (m³)"),
        new(["cm3","cm³","cc"],            0.001,        UnitCategory.Volume,      "세제곱센티미터 (cc)"),
        new(["gallon","gal","갤런"],       3.78541,      UnitCategory.Volume,      "갤런 (US, gal)"),
        new(["floz","fl.oz"],              0.0295735,    UnitCategory.Volume,      "액량온스 (fl.oz)"),
        new(["cup","컵"],                  0.236588,     UnitCategory.Volume,      "컵 (cup)"),
    ];

    private static readonly Dictionary<string, UnitCategory> CategoryKeywords =
        new(StringComparer.OrdinalIgnoreCase)
        {
            ["length"]  = UnitCategory.Length,      ["길이"]  = UnitCategory.Length,
            ["weight"]  = UnitCategory.Weight,      ["무게"]  = UnitCategory.Weight,
            ["mass"]    = UnitCategory.Weight,
            ["area"]    = UnitCategory.Area,         ["넓이"] = UnitCategory.Area,
            ["speed"]   = UnitCategory.Speed,        ["속도"] = UnitCategory.Speed,
            ["data"]    = UnitCategory.Data,         ["데이터"]= UnitCategory.Data,
            ["temp"]    = UnitCategory.Temperature,  ["온도"] = UnitCategory.Temperature,
            ["temperature"] = UnitCategory.Temperature,
            ["pressure"] = UnitCategory.Pressure,   ["압력"] = UnitCategory.Pressure,
            ["volume"]  = UnitCategory.Volume,       ["부피"] = UnitCategory.Volume,
        };

    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("단위 변환기",
                "예: unit 100 km m  /  unit 72 f c  /  unit 5 kg lb  /  unit 1 gb mb",
                null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("── 카테고리 ──", "", null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("unit length",      "길이 단위 (km·m·cm·ft·in·mi)",   null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("unit weight",      "무게 단위 (kg·g·lb·oz·근)",       null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("unit temp",        "온도 단위 (°C·°F·K)",             null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("unit area",        "넓이 단위 (m²·ha·acre·평)",        null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("unit speed",       "속도 단위 (km/h·mph·m/s·knot)",   null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("unit data",        "데이터 단위 (bit·B·KB·MB·GB·TB)", null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("unit pressure",    "압력 단위 (Pa·atm·bar·psi)",      null, null, Symbol: "\uE8EF"));
            items.Add(new LauncherItem("unit volume",      "부피 단위 (L·mL·m³·gallon·cup)",  null, null, Symbol: "\uE8EF"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        // 카테고리 목록
        if (parts.Length == 1 && CategoryKeywords.TryGetValue(parts[0], out var cat))
        {
            var catUnits = Units.Where(u => u.Cat == cat).ToList();
            items.Add(new LauncherItem($"{cat} 단위  {catUnits.Count}개",
                "예: unit 100 km m", null, null, Symbol: "\uE8EF"));
            foreach (var u in catUnits)
                items.Add(new LauncherItem(u.Display, string.Join(", ", u.Names), null, null, Symbol: "\uE8EF"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 변환: unit <값> <from> <to>
        if (parts.Length < 2)
        {
            items.Add(new LauncherItem("입력 형식",
                "unit <값> <단위> [대상단위]  예: unit 100 km m", null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        if (!double.TryParse(parts[0].Replace(",", ""), System.Globalization.NumberStyles.Any,
                System.Globalization.CultureInfo.InvariantCulture, out var value))
        {
            items.Add(new LauncherItem("숫자 형식 오류",
                "첫 번째 값이 숫자여야 합니다  예: unit 100 km m", null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var fromKey = parts[1].ToLowerInvariant();
        var fromDef = FindUnit(fromKey);
        if (fromDef == null)
        {
            items.Add(new LauncherItem($"'{parts[1]}' 단위를 찾을 수 없습니다",
                "unit length / weight / temp / area / speed / data 로 단위 목록 확인",
                null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 대상 단위 지정
        if (parts.Length >= 3)
        {
            var toKey = parts[2].ToLowerInvariant();
            var toDef = FindUnit(toKey);
            if (toDef == null)
            {
                items.Add(new LauncherItem($"'{parts[2]}' 단위를 찾을 수 없습니다",
                    "", null, null, Symbol: "\uE783"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }
            if (fromDef.Cat != toDef.Cat)
            {
                items.Add(new LauncherItem("카테고리 불일치",
                    $"{fromDef.Cat} ≠ {toDef.Cat} — 같은 종류끼리만 변환 가능",
                    null, null, Symbol: "\uE783"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }

            var result = Convert(value, fromDef, toDef);
            var label  = $"{FormatNum(value)} {fromDef.Names[0].ToUpper()}  =  {FormatNum(result)} {toDef.Names[0].ToUpper()}";
            items.Add(new LauncherItem(label, "Enter → 복사", null, ("copy", label), Symbol: "\uE8EF"));
            items.Add(new LauncherItem($"{FormatNum(result)} {toDef.Names[0].ToUpper()}", toDef.Display,
                null, ("copy", FormatNum(result)), Symbol: "\uE8EF"));
        }
        else
        {
            // 같은 카테고리 모든 단위로 변환
            var sameCat = Units.Where(u => u.Cat == fromDef.Cat && u != fromDef).ToList();
            items.Add(new LauncherItem($"{FormatNum(value)} {fromDef.Names[0].ToUpper()} 변환 결과",
                fromDef.Display, null, null, Symbol: "\uE8EF"));
            foreach (var toDef in sameCat)
            {
                var result = Convert(value, fromDef, toDef);
                var label  = $"{FormatNum(result)} {toDef.Names[0].ToUpper()}";
                items.Add(new LauncherItem(label, toDef.Display, null, ("copy", FormatNum(result)), 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("Unit", "클립보드에 복사했습니다.");
            }
            catch { /* 비핵심 */ }
        }
        return Task.CompletedTask;
    }

    // ── 변환 로직 ─────────────────────────────────────────────────────────────

    private static double Convert(double value, UnitDef from, UnitDef to)
    {
        if (from.Cat == UnitCategory.Temperature)
            return ConvertTemp(value, from.Names[0].ToLowerInvariant(), to.Names[0].ToLowerInvariant());

        // 선형 변환: value × from.ToBase / to.ToBase
        return value * from.ToBase / to.ToBase;
    }

    private static double ConvertTemp(double value, string from, string to)
    {
        // 먼저 °C로
        var celsius = from switch
        {
            "c" or "°c" => value,
            "f" or "°f" => (value - 32) * 5 / 9,
            "k"          => value - 273.15,
            _            => value,
        };
        // °C에서 목표로
        return to switch
        {
            "c" or "°c" => celsius,
            "f" or "°f" => celsius * 9 / 5 + 32,
            "k"          => celsius + 273.15,
            _            => celsius,
        };
    }

    private static UnitDef? FindUnit(string key) =>
        Units.FirstOrDefault(u => u.Names.Any(n => n.Equals(key, StringComparison.OrdinalIgnoreCase)));

    private static string FormatNum(double v)
    {
        if (double.IsNaN(v) || double.IsInfinity(v)) return v.ToString();
        if (Math.Abs(v) >= 1e12 || (Math.Abs(v) < 1e-4 && v != 0))
            return v.ToString("E3", System.Globalization.CultureInfo.InvariantCulture);
        if (v == Math.Floor(v) && Math.Abs(v) < 1e9)
            return $"{v:N0}";
        return v.ToString("G6", System.Globalization.CultureInfo.InvariantCulture);
    }
}
