using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// L19-3: IP 주소 유틸리티 핸들러. "ip" 프리픽스로 사용합니다.
///
/// 예: ip                     → 로컬 IP 주소 목록
///     ip my                  → 전체 어댑터 IP 목록
///     ip 192.168.1.100       → IP 분류·타입·이진 표현
///     ip 10.0.0.0/8          → CIDR 네트워크 정보
///     ip 192.168.1.0/24      → 네트워크 주소·브로드캐스트·호스트 범위·수
///     ip range 192.168.1.1 192.168.1.100 → 범위 내 IP 수
///     ip bin 192.168.1.1     → 이진 표현
///     ip hex 192.168.1.1     → 16진수 표현
///     ip int 192.168.1.1     → 정수(uint32) 표현
///     ip from 3232235777     → 정수 → IP 주소
/// Enter → 값 복사.
/// </summary>
public class IpInfoHandler : IActionHandler
{
    public string? Prefix => "ip";

    public PluginMetadata Metadata => new(
        "IP",
        "IP 주소 유틸리티 — 분류·CIDR·이진·16진·정수 변환",
        "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("IP 주소 유틸리티",
                "ip my  /  ip 192.168.1.1  /  ip 10.0.0.0/8  /  ip bin/hex/int <IP>",
                null, null, Symbol: "\uE968"));
            BuildLocalIpItems(items, brief: true);
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        // ip my
        if (sub is "my" or "local" or "내" or "로컬")
        {
            items.Add(new LauncherItem("로컬 네트워크 어댑터 IP 목록", "", null, null, Symbol: "\uE968"));
            BuildLocalIpItems(items, brief: false);
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // ip range <start> <end>
        if (sub == "range" && parts.Length >= 3)
        {
            if (IPAddress.TryParse(parts[1], out var start) &&
                IPAddress.TryParse(parts[2], out var end) &&
                start.AddressFamily == AddressFamily.InterNetwork &&
                end.AddressFamily == AddressFamily.InterNetwork)
            {
                var s = IpToUint(start);
                var e = IpToUint(end);
                if (s > e) (s, e) = (e, s);
                var count = e - s + 1;
                items.Add(new LauncherItem($"{UintToIp(s)}  ~  {UintToIp(e)}",
                    $"IP 수: {count:N0}개", null, null, Symbol: "\uE968"));
                items.Add(CopyItem("시작 IP",    UintToIp(s)));
                items.Add(CopyItem("끝 IP",      UintToIp(e)));
                items.Add(CopyItem("IP 개수",    count.ToString("N0")));
            }
            else
            {
                items.Add(ErrorItem("올바른 IPv4 주소를 입력하세요"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // ip from <uint>
        if (sub == "from" && parts.Length >= 2)
        {
            if (uint.TryParse(parts[1], out var uval))
            {
                var ip = UintToIp(uval);
                items.Add(new LauncherItem($"{uval}  →  {ip}", "정수 → IPv4 변환", null, ("copy", ip), Symbol: "\uE968"));
                items.Add(CopyItem("IP 주소", ip));
                items.Add(CopyItem("이진",    ToBinary(uval)));
                items.Add(CopyItem("16진수",  $"0x{uval:X8}"));
            }
            else items.Add(ErrorItem("올바른 32비트 정수를 입력하세요"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // ip bin/hex/int <IP>
        if (sub is "bin" or "binary" or "이진" or "hex" or "int" or "integer")
        {
            if (parts.Length >= 2 && IPAddress.TryParse(parts[1], out var ip4) &&
                ip4.AddressFamily == AddressFamily.InterNetwork)
            {
                BuildConversionItems(items, ip4);
            }
            else items.Add(ErrorItem("올바른 IPv4 주소를 입력하세요"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // CIDR: 10.0.0.0/8
        if (parts[0].Contains('/'))
        {
            BuildCidrItems(items, parts[0]);
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 단순 IP 주소
        if (IPAddress.TryParse(parts[0], out var addr))
        {
            if (addr.AddressFamily == AddressFamily.InterNetwork)
                BuildIpInfoItems(items, addr);
            else if (addr.AddressFamily == AddressFamily.InterNetworkV6)
                BuildIpv6Items(items, addr);
            else
                items.Add(ErrorItem("IPv4 또는 IPv6 주소를 입력하세요"));
        }
        else
        {
            items.Add(new LauncherItem($"인식할 수 없는 입력: '{parts[0]}'",
                "ip 192.168.1.1  /  ip 10.0.0.0/8  /  ip my  /  ip bin 1.2.3.4",
                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("IP", "클립보드에 복사했습니다.");
            }
            catch { }
        }
        return Task.CompletedTask;
    }

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

    private static void BuildLocalIpItems(List<LauncherItem> items, bool brief)
    {
        try
        {
            var ifaces = NetworkInterface.GetAllNetworkInterfaces()
                .Where(n => n.OperationalStatus == OperationalStatus.Up &&
                            n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
                .ToList();

            if (ifaces.Count == 0)
            {
                items.Add(new LauncherItem("활성 네트워크 어댑터 없음", "", null, null, Symbol: "\uE968"));
                return;
            }

            foreach (var nic in ifaces)
            {
                var ipProps = nic.GetIPProperties();
                var ipv4s   = ipProps.UnicastAddresses
                    .Where(u => u.Address.AddressFamily == AddressFamily.InterNetwork)
                    .ToList();

                if (ipv4s.Count == 0) continue;

                if (!brief)
                    items.Add(new LauncherItem($"── {nic.Name} ──",
                        $"{nic.Description}  ({nic.Speed / 1_000_000} Mbps)", null, null, Symbol: "\uE968"));

                foreach (var uni in ipv4s)
                {
                    var mask  = uni.IPv4Mask?.ToString() ?? "";
                    var cidr  = MaskToCidr(uni.IPv4Mask);
                    var label = brief ? uni.Address.ToString() : $"{uni.Address}/{cidr}";
                    items.Add(new LauncherItem(label,
                        brief ? nic.Name : $"마스크: {mask}  CIDR: /{cidr}  ({ClassifyIp(uni.Address)})",
                        null, ("copy", uni.Address.ToString()), Symbol: "\uE968"));
                }

                if (!brief)
                {
                    var gateways = ipProps.GatewayAddresses
                        .Where(g => g.Address.AddressFamily == AddressFamily.InterNetwork)
                        .Select(g => g.Address.ToString()).ToList();
                    if (gateways.Count > 0)
                        items.Add(new LauncherItem("게이트웨이",
                            string.Join(", ", gateways), null, ("copy", gateways[0]), Symbol: "\uE968"));
                }
            }
        }
        catch (Exception ex)
        {
            items.Add(ErrorItem($"네트워크 정보 조회 오류: {ex.Message}"));
        }
    }

    private static void BuildIpInfoItems(List<LauncherItem> items, IPAddress ip)
    {
        var type  = ClassifyIp(ip);
        var uval  = IpToUint(ip);
        items.Add(new LauncherItem($"{ip}  ({type})",
            $"클래스: {GetClass(uval)}  · Enter 복사", null, ("copy", ip.ToString()), Symbol: "\uE968"));
        items.Add(CopyItem("IP 주소", ip.ToString()));
        items.Add(CopyItem("분류",    type));
        items.Add(CopyItem("클래스",  GetClass(uval)));
        items.Add(CopyItem("이진",    ToBinary(uval)));
        items.Add(CopyItem("16진수",  $"0x{uval:X8}"));
        items.Add(CopyItem("정수",    uval.ToString()));
        BuildConversionItems(items, ip);
    }

    private static void BuildConversionItems(List<LauncherItem> items, IPAddress ip)
    {
        var uval = IpToUint(ip);
        var bin  = ToBinary(uval);
        items.Add(new LauncherItem("── 변환 ──", "", null, null, Symbol: "\uE968"));
        items.Add(CopyItem("이진 (32bit)", bin));
        items.Add(CopyItem("이진 (점구분)", ToBinaryDotted(uval)));
        items.Add(CopyItem("16진수",        $"0x{uval:X8}"));
        items.Add(CopyItem("정수 (uint32)", uval.ToString()));
    }

    private static void BuildIpv6Items(List<LauncherItem> items, IPAddress ip)
    {
        items.Add(new LauncherItem($"{ip}", "IPv6 주소", null, ("copy", ip.ToString()), Symbol: "\uE968"));
        items.Add(CopyItem("전체 표기", ip.ToString()));
        var expanded = ExpandIpv6(ip);
        if (expanded != ip.ToString())
            items.Add(CopyItem("확장 표기", expanded));
    }

    private static void BuildCidrItems(List<LauncherItem> items, string cidr)
    {
        var slash = cidr.IndexOf('/');
        if (slash < 0) { items.Add(ErrorItem("올바른 CIDR 형식: 10.0.0.0/8")); return; }

        var ipStr   = cidr[..slash];
        var lenStr  = cidr[(slash + 1)..];

        if (!IPAddress.TryParse(ipStr, out var addr) || addr.AddressFamily != AddressFamily.InterNetwork ||
            !int.TryParse(lenStr, out var prefix) || prefix < 0 || prefix > 32)
        {
            items.Add(ErrorItem("올바른 IPv4 CIDR을 입력하세요 (예: 192.168.1.0/24)"));
            return;
        }

        var mask        = CidrToMask(prefix);
        var maskIp      = UintToIp(mask);
        var wildcard    = ~mask;
        var network     = IpToUint(addr) & mask;
        var broadcast   = network | wildcard;
        var hostCount   = prefix < 31 ? (broadcast - network - 1) : (broadcast - network + 1);
        var firstHost   = prefix < 31 ? network + 1 : network;
        var lastHost    = prefix < 31 ? broadcast - 1 : broadcast;

        items.Add(new LauncherItem($"{cidr}  →  {IpToHostCount(prefix)}",
            $"네트워크: {UintToIp(network)}  브로드캐스트: {UintToIp(broadcast)}", null, null, Symbol: "\uE968"));
        items.Add(CopyItem("네트워크 주소",        UintToIp(network)));
        items.Add(CopyItem("브로드캐스트 주소",    UintToIp(broadcast)));
        items.Add(CopyItem("서브넷 마스크",        maskIp.ToString()));
        items.Add(CopyItem("와일드카드 마스크",    UintToIp(wildcard)));
        items.Add(CopyItem("첫 번째 호스트",       UintToIp(firstHost)));
        items.Add(CopyItem("마지막 호스트",        UintToIp(lastHost)));
        items.Add(CopyItem("호스트 수",            $"{hostCount:N0}"));
        items.Add(CopyItem("CIDR 표기",            $"{UintToIp(network)}/{prefix}"));
        items.Add(CopyItem("이진 마스크",          ToBinaryDotted(mask)));
    }

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

    private static string ClassifyIp(IPAddress ip)
    {
        var u = IpToUint(ip);
        if (u == 0x7F000001u)                         return "루프백 (localhost)";
        if ((u & 0xFF000000u) == 0x7F000000u)         return "루프백";
        if ((u & 0xFF000000u) == 0x0A000000u)         return "사설 (Class A: 10.x.x.x)";
        if ((u & 0xFFF00000u) == 0xAC100000u)         return "사설 (Class B: 172.16-31.x.x)";
        if ((u & 0xFFFF0000u) == 0xC0A80000u)         return "사설 (Class C: 192.168.x.x)";
        if ((u & 0xFFFF0000u) == 0xA9FE0000u)         return "링크-로컬 (APIPA: 169.254.x.x)";
        if ((u & 0xF0000000u) == 0xE0000000u)         return "멀티캐스트 (224.x.x.x)";
        if (u == 0xFFFFFFFFu)                         return "브로드캐스트";
        if (u == 0u)                                  return "0.0.0.0 (비지정)";
        return "공인 IP";
    }

    private static string GetClass(uint u)
    {
        var b = (u >> 24) & 0xFF;
        return b switch
        {
            <= 127 => "Class A",
            <= 191 => "Class B",
            <= 223 => "Class C",
            <= 239 => "Class D (멀티캐스트)",
            _      => "Class E (예약)"
        };
    }

    private static uint IpToUint(IPAddress ip)
    {
        var bytes = ip.GetAddressBytes();
        if (bytes.Length != 4) return 0;
        return (uint)((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
    }

    private static string UintToIp(uint u) =>
        $"{(u >> 24) & 0xFF}.{(u >> 16) & 0xFF}.{(u >> 8) & 0xFF}.{u & 0xFF}";

    private static string ToBinary(uint u) => Convert.ToString((long)u, 2).PadLeft(32, '0');

    private static string ToBinaryDotted(uint u)
    {
        var b = ToBinary(u);
        return $"{b[..8]}.{b[8..16]}.{b[16..24]}.{b[24..]}";
    }

    private static uint CidrToMask(int prefix) =>
        prefix == 0 ? 0u : (0xFFFFFFFFu << (32 - prefix));

    private static int MaskToCidr(IPAddress? mask)
    {
        if (mask == null) return 0;
        var u = IpToUint(mask);
        int c = 0;
        while ((u & 0x80000000u) != 0) { c++; u <<= 1; }
        return c;
    }

    private static string IpToHostCount(int prefix) =>
        prefix switch
        {
            32 => "1 IP (단일 호스트)",
            31 => "2 IP (P2P 링크)",
            30 => "4 IP (2 호스트)",
            _  => $"{(1L << (32 - prefix)):N0} IP ({(1L << (32 - prefix)) - 2:N0} 호스트)"
        };

    private static string ExpandIpv6(IPAddress ip)
    {
        var bytes   = ip.GetAddressBytes();
        var groups  = new string[8];
        for (int i = 0; i < 8; i++)
            groups[i] = $"{bytes[i * 2]:X2}{bytes[i * 2 + 1]:X2}";
        return string.Join(":", groups).ToLowerInvariant();
    }

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

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