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

namespace AxCopilot.Handlers;

/// <summary>
/// L9-4: 진수 변환기 핸들러. "base" 프리픽스로 사용합니다.
///
/// 예: base 255             → 10진수 → 2/8/16진수 동시 변환
///     base 0xFF            → 16진수 → 10/2/8진수
///     base 0b11111111      → 2진수  → 10/8/16진수
///     base 0o377           → 8진수  → 10/2/16진수
///     base 255 to hex      → 10→16진수 결과만
///     base ascii 65        → ASCII 코드 변환 (65 = 'A')
///     base ascii A         → 문자 → ASCII 코드
/// Enter → 결과를 클립보드에 복사.
/// </summary>
public class BaseConvertHandler : IActionHandler
{
    public string? Prefix => "base";

    public PluginMetadata Metadata => new(
        "BaseConvert",
        "진수 변환기 — 2 · 8 · 10 · 16진수 · ASCII",
        "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(
                "진수 변환기",
                "예: base 255  /  base 0xFF  /  base 0b1010  /  base ascii 65",
                null, null, Symbol: "\uE8C4"));
            items.Add(new LauncherItem("base 255",       "10진수 → 2/8/16진수", null, null, Symbol: "\uE8C4"));
            items.Add(new LauncherItem("base 0xFF",      "16진수 → 10/2/8진수", null, null, Symbol: "\uE8C4"));
            items.Add(new LauncherItem("base 0b1111",    "2진수  → 10/8/16진수", null, null, Symbol: "\uE8C4"));
            items.Add(new LauncherItem("base ascii 65",  "ASCII 코드 변환", null, null, Symbol: "\uE8C4"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        // ASCII 모드
        if (parts[0].Equals("ascii", StringComparison.OrdinalIgnoreCase) && parts.Length >= 2)
        {
            items.AddRange(BuildAsciiItems(string.Join(" ", parts.Skip(1))));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // "to" 지정 변환 모드: base 255 to hex
        if (parts.Length >= 3 && parts[1].Equals("to", StringComparison.OrdinalIgnoreCase))
        {
            if (TryParseNumber(parts[0], out var val))
            {
                var targetBase = parts[2].ToLowerInvariant();
                var result     = ConvertToBase(val, targetBase);
                if (result != null)
                {
                    items.Add(new LauncherItem(
                        result,
                        $"{parts[0]} → {targetBase}",
                        null,
                        ("copy", result),
                        Symbol: "\uE8C4"));
                }
                else
                {
                    items.Add(new LauncherItem("알 수 없는 진수",
                        "bin/oct/dec/hex 중 하나를 지정하세요", null, null, Symbol: "\uE783"));
                }
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 전체 변환 모드
        if (TryParseNumber(parts[0], out var number))
        {
            items.AddRange(BuildConversionItems(number, parts[0]));
        }
        else
        {
            // ASCII 문자열로 시도
            if (q.Length <= 8 && q.All(c => c >= 32 && c < 128))
            {
                items.AddRange(BuildAsciiItems(q));
            }
            else
            {
                items.Add(new LauncherItem("파싱 실패", $"'{q}'을(를) 숫자로 해석할 수 없습니다", null, null, Symbol: "\uE783"));
            }
        }

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

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

    private static IEnumerable<LauncherItem> BuildConversionItems(long val, string inputStr)
    {
        var inputBase = DetectBase(inputStr);

        yield return new LauncherItem(
            $"{val}",
            $"10진수 (입력: {inputStr})",
            null,
            ("copy", val.ToString()),
            Symbol: "\uE8C4");

        var hex = $"0x{val:X}";
        yield return new LauncherItem(hex, "16진수 (HEX)",  null, ("copy", hex),  Symbol: "\uE8C4");

        var bin = val >= 0 ? $"0b{Convert.ToString(val, 2)}" : $"-0b{Convert.ToString(-val, 2)}";
        yield return new LauncherItem(bin, "2진수 (BIN)",   null, ("copy", bin),  Symbol: "\uE8C4");

        var oct = val >= 0 ? $"0o{Convert.ToString(val, 8)}" : $"-0o{Convert.ToString(-val, 8)}";
        yield return new LauncherItem(oct, "8진수 (OCT)",   null, ("copy", oct),  Symbol: "\uE8C4");

        // 2진수 그룹핑 표시 (4비트 단위)
        if (val >= 0 && val <= 0xFFFFFFFF)
        {
            var binRaw    = Convert.ToString(val, 2).PadLeft((Convert.ToString(val, 2).Length + 3) / 4 * 4, '0');
            var binGrouped = string.Join(" ", Enumerable.Range(0, binRaw.Length / 4)
                                                         .Select(i => binRaw.Substring(i * 4, 4)));
            yield return new LauncherItem(binGrouped, "2진수 (4비트 그룹)", null, ("copy", binGrouped), Symbol: "\uE8C4");
        }

        // ASCII 문자 (0~127 범위)
        if (val is >= 32 and <= 126)
        {
            var ch = (char)val;
            yield return new LauncherItem($"'{ch}'", $"ASCII 문자 (코드 {val})", null, ("copy", ch.ToString()), Symbol: "\uE8C4");
        }
    }

    private static IEnumerable<LauncherItem> BuildAsciiItems(string input)
    {
        // 숫자 → 문자
        if (long.TryParse(input, out var code) && code >= 0 && code <= 127)
        {
            var ch = (char)code;
            yield return new LauncherItem(
                $"'{ch}'",
                $"ASCII {code} = '{ch}'",
                null,
                ("copy", ch.ToString()),
                Symbol: "\uE8C4");

            yield return new LauncherItem($"HEX: 0x{code:X2}", "16진수", null, ("copy", $"0x{code:X2}"), Symbol: "\uE8C4");
            yield return new LauncherItem($"BIN: {Convert.ToString(code, 2).PadLeft(8, '0')}", "2진수", null, ("copy", Convert.ToString(code, 2).PadLeft(8, '0')), Symbol: "\uE8C4");
            yield break;
        }

        // 문자/문자열 → 코드
        foreach (var c in input.Where(c => c < 128))
        {
            var codeVal = (int)c;
            yield return new LauncherItem(
                $"'{c}'  =  {codeVal}",
                $"HEX: 0x{codeVal:X2}  BIN: {Convert.ToString(codeVal, 2).PadLeft(8, '0')}",
                null,
                ("copy", codeVal.ToString()),
                Symbol: "\uE8C4");
        }
    }

    private static bool TryParseNumber(string s, out long result)
    {
        result = 0;
        if (string.IsNullOrEmpty(s)) return false;

        // 0x prefix → 16진수
        if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase) || s.StartsWith("0X", StringComparison.OrdinalIgnoreCase))
        {
            return long.TryParse(s[2..], System.Globalization.NumberStyles.HexNumber, null, out result);
        }
        // 0b prefix → 2진수
        if (s.StartsWith("0b", StringComparison.OrdinalIgnoreCase))
        {
            try { result = Convert.ToInt64(s[2..], 2); return true; }
            catch { return false; }
        }
        // 0o prefix → 8진수
        if (s.StartsWith("0o", StringComparison.OrdinalIgnoreCase))
        {
            try { result = Convert.ToInt64(s[2..], 8); return true; }
            catch { return false; }
        }
        // 순수 16진수 (0-9, A-F)
        if (s.Length >= 2 && s.All(c => "0123456789ABCDEFabcdef".Contains(c)) && !s.All(char.IsDigit))
        {
            return long.TryParse(s, System.Globalization.NumberStyles.HexNumber, null, out result);
        }
        // 10진수
        return long.TryParse(s, out result);
    }

    private static string? ConvertToBase(long val, string targetBase) => targetBase switch
    {
        "hex" or "16" or "h" => $"0x{val:X}",
        "bin" or "2"  or "b" => val >= 0 ? $"0b{Convert.ToString(val, 2)}" : $"-0b{Convert.ToString(-val, 2)}",
        "oct" or "8"  or "o" => val >= 0 ? $"0o{Convert.ToString(val, 8)}" : $"-0o{Convert.ToString(-val, 8)}",
        "dec" or "10" or "d" => val.ToString(),
        _ => null,
    };

    private static string DetectBase(string s)
    {
        if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) return "HEX";
        if (s.StartsWith("0b", StringComparison.OrdinalIgnoreCase)) return "BIN";
        if (s.StartsWith("0o", StringComparison.OrdinalIgnoreCase)) return "OCT";
        return "DEC";
    }
}
