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

namespace AxCopilot.Handlers;

/// <summary>
/// L20-1: 16진수·바이트 변환기 핸들러. "hex" 프리픽스로 사용합니다.
///
/// 예: hex                    → 클립보드 텍스트 → hex 변환
///     hex hello              → "hello" → 68 65 6C 6C 6F
///     hex 68656c6c6f         → hex → "hello" 디코딩
///     hex dump hello world   → 헥스 덤프 형식 (오프셋·hex·ASCII)
///     hex 0xFF               → 0xFF = 255 (십진수·이진수·문자)
///     hex add 0x1A 0x2B      → hex 덧셈
///     hex xor 0xAB 0xCD      → bitwise XOR
///     hex and 0xFF 0x0F      → bitwise AND
///     hex or 0xA0 0x0F       → bitwise OR
///     hex not 0xFF           → bitwise NOT (8비트)
///     hex bytes <n>          → n바이트 크기 단위 표시 (KB·MB·GB)
/// Enter → 결과 복사.
/// </summary>
public class HexHandler : IActionHandler
{
    public string? Prefix => "hex";

    public PluginMetadata Metadata => new(
        "Hex",
        "16진수·바이트 변환기 — 텍스트↔hex·덤프·비트연산·크기 단위",
        "1.0",
        "AX");

    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))
        {
            items.Add(new LauncherItem("16진수·바이트 변환기",
                "hex <텍스트>  /  hex <hexstr>  /  hex dump <text>  /  hex 0xFF  /  hex bytes <n>",
                null, null, Symbol: "\uE8EF"));
            if (!string.IsNullOrWhiteSpace(clipboard))
                items.AddRange(BuildFromText(clipboard!, brief: true));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        // hex dump
        if (sub == "dump")
        {
            var text = parts.Length > 1 ? string.Join(" ", parts[1..]) : clipboard ?? "";
            if (string.IsNullOrEmpty(text))
            { items.Add(ErrorItem("텍스트를 입력하거나 클립보드에 복사하세요")); }
            else
                items.AddRange(BuildDump(text));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // hex bytes <n>
        if (sub == "bytes" && parts.Length >= 2 &&
            long.TryParse(parts[1], out var byteCount))
        {
            items.AddRange(BuildByteSize(byteCount));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // hex add/xor/and/or/not (비트 연산)
        if (sub is "add" or "xor" or "and" or "or" or "not")
        {
            items.AddRange(BuildBitOp(sub, parts));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 단일 hex 값 (0xFF, 0xAB, FF, AB...)
        if (TryParseHexValue(parts[0], out var hexVal))
        {
            items.AddRange(BuildFromHexValue(hexVal, parts[0]));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 순수 hex 문자열인지 판단 (2자 이상, 모두 hex digit, 짝수 길이)
        var raw = parts[0].Replace(" ", "").Replace("-", "").Replace(":", "");
        if (raw.Length >= 2 && raw.Length % 2 == 0 && IsAllHex(raw))
        {
            items.AddRange(BuildFromHexString(raw));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 일반 텍스트 → hex 변환
        var input = string.Join(" ", parts);
        items.AddRange(BuildFromText(input, brief: false));
        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("Hex", "클립보드에 복사했습니다.");
            }
            catch { }
        }
        return Task.CompletedTask;
    }

    // ── 빌더 ────────────────────────────────────────────────────────────────

    private static IEnumerable<LauncherItem> BuildFromText(string text, bool brief)
    {
        var bytes = Encoding.UTF8.GetBytes(text);
        var hex   = BitConverter.ToString(bytes).Replace("-", "");
        var spaced = string.Join(" ", Enumerable.Range(0, hex.Length / 2)
            .Select(i => hex.Substring(i * 2, 2)));

        yield return new LauncherItem(spaced,
            $"텍스트 → Hex  ({bytes.Length} bytes)  · Enter 복사",
            null, ("copy", spaced), Symbol: "\uE8EF");

        if (!brief)
        {
            yield return CopyItem("공백 없음", hex);
            yield return CopyItem("소문자",    hex.ToLowerInvariant());
            yield return CopyItem("0x 접두사", string.Join(" ", Enumerable.Range(0, hex.Length / 2)
                .Select(i => "0x" + hex.Substring(i * 2, 2))));
            yield return CopyItem("바이트 수", $"{bytes.Length} bytes");
            var b64 = Convert.ToBase64String(bytes);
            yield return CopyItem("Base64",    b64);
        }
    }

    private static IEnumerable<LauncherItem> BuildFromHexString(string hex)
    {
        hex = hex.ToUpperInvariant();
        byte[]? bytes = null;
        string? parseError = null;
        try
        {
            bytes = Enumerable.Range(0, hex.Length / 2)
                .Select(i => Convert.ToByte(hex.Substring(i * 2, 2), 16))
                .ToArray();
        }
        catch { parseError = "올바른 hex 문자열이 아닙니다"; }

        if (parseError != null)
        {
            yield return ErrorItem(parseError);
            yield break;
        }

        var safeBytes = bytes!;
        var utf8  = TrySafeUtf8(safeBytes);
        var ascii = TrySafeAscii(safeBytes);

        yield return new LauncherItem($"Hex → 텍스트",
            $"{safeBytes.Length} bytes  · UTF-8 디코딩",
            null, null, Symbol: "\uE8EF");

        if (utf8 != null)  yield return CopyItem("UTF-8 텍스트", utf8);
        if (ascii != null && ascii != utf8) yield return CopyItem("ASCII 텍스트", ascii);

        yield return CopyItem("바이트 수", $"{safeBytes.Length}");
        // 숫자 해석 (최대 8바이트)
        if (safeBytes.Length <= 8)
        {
            var padded = new byte[8];
            Buffer.BlockCopy(safeBytes, 0, padded, 8 - safeBytes.Length, safeBytes.Length);
            var bigEndian = BitConverter.IsLittleEndian
                ? BitConverter.ToUInt64(padded.Reverse().ToArray())
                : BitConverter.ToUInt64(padded);
            yield return CopyItem($"정수 (big-endian)", bigEndian.ToString());
        }
    }

    private static IEnumerable<LauncherItem> BuildFromHexValue(ulong val, string original)
    {
        yield return new LauncherItem($"{original}  =  {val}",
            $"16진수 → 십진수  · Enter 복사",
            null, ("copy", val.ToString()), Symbol: "\uE8EF");
        yield return CopyItem("십진수",   val.ToString());
        yield return CopyItem("16진수",   $"0x{val:X}");
        yield return CopyItem("8진수",    $"0o{Convert.ToString((long)val, 8)}");
        yield return CopyItem("이진수",   $"0b{Convert.ToString((long)val, 2)}");
        if (val <= 127) yield return CopyItem("ASCII 문자", ((char)val).ToString());
        yield return CopyItem("NOT (64bit)", $"0x{(~val):X16}");
    }

    private static IEnumerable<LauncherItem> BuildDump(string text)
    {
        var bytes   = Encoding.UTF8.GetBytes(text);
        var lines   = new List<string>();
        var sb      = new StringBuilder();

        for (int i = 0; i < bytes.Length; i += 16)
        {
            var chunk = bytes.Skip(i).Take(16).ToArray();
            var hexPart  = string.Join(" ", chunk.Select(b => $"{b:X2}")).PadRight(47);
            var asciiPart = new string(chunk.Select(b => b >= 32 && b < 127 ? (char)b : '.').ToArray());
            var line = $"{i:X8}  {hexPart}  |{asciiPart}|";
            lines.Add(line);
        }

        var dump = string.Join("\n", lines);
        yield return new LauncherItem("헥스 덤프",
            $"{bytes.Length} bytes  · {lines.Count} 행", null, ("copy", dump), Symbol: "\uE8EF");
        foreach (var line in lines.Take(8))
            yield return new LauncherItem(line, "", null, ("copy", line), Symbol: "\uE8EF");
        if (lines.Count > 8)
            yield return new LauncherItem($"... ({lines.Count - 8}행 더 있음)",
                "전체 복사하려면 상단 항목 Enter", null, null, Symbol: "\uE8EF");
    }

    private static IEnumerable<LauncherItem> BuildBitOp(string op, string[] parts)
    {
        if (op == "not")
        {
            if (parts.Length < 2 || !TryParseHexValue(parts[1], out var v))
            { yield return ErrorItem("예: hex not 0xFF"); yield break; }
            var r8  = (byte)(~(byte)v);
            var r16 = (ushort)(~(ushort)v);
            yield return new LauncherItem($"NOT 0x{v:X} = 0x{r8:X2} (8bit) / 0x{r16:X4} (16bit)",
                "비트 반전", null, ("copy", $"0x{r8:X2}"), Symbol: "\uE8EF");
            yield return CopyItem("NOT 8bit",  $"0x{r8:X2}  ({r8})");
            yield return CopyItem("NOT 16bit", $"0x{r16:X4}  ({r16})");
            yield return CopyItem("NOT 64bit", $"0x{(~v):X16}");
            yield break;
        }
        if (parts.Length < 3 || !TryParseHexValue(parts[1], out var a) || !TryParseHexValue(parts[2], out var b))
        { yield return ErrorItem($"예: hex {op} 0xAB 0xCD"); yield break; }

        ulong result = op switch
        {
            "add" => a + b,
            "xor" => a ^ b,
            "and" => a & b,
            "or"  => a | b,
            _     => 0
        };
        var symbol = op switch { "add" => "+", "xor" => "^", "and" => "&", "or" => "|", _ => "?" };
        yield return new LauncherItem($"0x{a:X} {symbol} 0x{b:X} = 0x{result:X}",
            $"{a} {symbol} {b} = {result}  · Enter 복사",
            null, ("copy", $"0x{result:X}"), Symbol: "\uE8EF");
        yield return CopyItem("16진수", $"0x{result:X}");
        yield return CopyItem("십진수", result.ToString());
        yield return CopyItem("이진수", $"0b{Convert.ToString((long)result, 2)}");
    }

    private static IEnumerable<LauncherItem> BuildByteSize(long bytes)
    {
        yield return new LauncherItem($"{bytes:N0} bytes",
            "크기 단위 변환  · Enter 복사", null, ("copy", bytes.ToString()), Symbol: "\uE8EF");
        yield return CopyItem("Bytes",     $"{bytes:N0}");
        yield return CopyItem("KB (1000)", $"{bytes / 1000.0:F3}");
        yield return CopyItem("KiB (1024)",$"{bytes / 1024.0:F3}");
        yield return CopyItem("MB (1000)", $"{bytes / 1_000_000.0:F3}");
        yield return CopyItem("MiB (1024)",$"{bytes / 1_048_576.0:F3}");
        yield return CopyItem("GB (1000)", $"{bytes / 1_000_000_000.0:F4}");
        yield return CopyItem("GiB (1024)",$"{bytes / 1_073_741_824.0:F4}");
        if (bytes >= 1_000_000_000_000L)
            yield return CopyItem("TB (1000)", $"{bytes / 1_000_000_000_000.0:F4}");
    }

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

    private static bool TryParseHexValue(string s, out ulong val)
    {
        val = 0;
        s   = s.Trim();
        if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
            return ulong.TryParse(s[2..], System.Globalization.NumberStyles.HexNumber, null, out val);
        if (s.StartsWith("0X", StringComparison.OrdinalIgnoreCase))
            return ulong.TryParse(s[2..], System.Globalization.NumberStyles.HexNumber, null, out val);
        return false;
    }

    private static bool IsAllHex(string s) =>
        s.All(c => c is >= '0' and <= '9' or >= 'a' and <= 'f' or >= 'A' and <= 'F');

    private static string? TrySafeUtf8(byte[] b)
    {
        try { var s = Encoding.UTF8.GetString(b); return s; }
        catch { return null; }
    }

    private static string? TrySafeAscii(byte[] b)
    {
        try { return Encoding.ASCII.GetString(b); }
        catch { return null; }
    }

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

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