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

namespace AxCopilot.Handlers;

/// <summary>
/// L9-2: IP 서브넷 계산기 핸들러. "subnet" 프리픽스로 사용합니다.
///
/// 예: subnet 192.168.1.0/24   → 네트워크 정보 전체 표시
///     subnet 10.0.0.5/24      → 해당 IP가 속한 서브넷 분석
///     subnet 255.255.255.0     → 서브넷 마스크 → CIDR 변환
///     subnet 192.168.1.0 24   → 슬래시 없이 입력 가능
///     subnet range 192.168.1.10-50 → IP 범위 정보
/// Enter → 결과를 클립보드에 복사.
/// </summary>
public class SubnetHandler : IActionHandler
{
    public string? Prefix => "subnet";

    public PluginMetadata Metadata => new(
        "Subnet",
        "IP 서브넷 계산기 — CIDR · 네트워크 · 호스트 범위",
        "1.0",
        "AX");

    // 자주 쓰는 CIDR 참조표
    private static readonly (int Prefix, int Hosts, string Desc)[] CidrRef =
    [
        (24, 254,      "/24  — 클래스 C (254 호스트)"),
        (25, 126,      "/25  — 128개 분할 (126 호스트)"),
        (26, 62,       "/26  — 64개 분할 (62 호스트)"),
        (27, 30,       "/27  — 32개 분할 (30 호스트)"),
        (28, 14,       "/28  — 16개 분할 (14 호스트)"),
        (29, 6,        "/29  — 8개 분할 (6 호스트)"),
        (30, 2,        "/30  — 포인트-투-포인트 (2 호스트)"),
        (23, 510,      "/23  — 512개 블록 (510 호스트)"),
        (22, 1022,     "/22  — 1024개 블록 (1022 호스트)"),
        (20, 4094,     "/20  — 4096개 블록 (4094 호스트)"),
        (16, 65534,    "/16  — 클래스 B (65534 호스트)"),
        (8,  16777214, "/8   — 클래스 A (16M 호스트)"),
    ];

    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(
                "서브넷 계산기",
                "예: subnet 192.168.1.0/24  또는  subnet 10.0.0.5 24",
                null, null, Symbol: "\uE968"));

            // CIDR 참조표 일부
            foreach (var (cidrLen, hosts, desc) in CidrRef.Take(6))
            {
                items.Add(new LauncherItem(
                    desc,
                    $"호스트: {hosts:N0}개",
                    null,
                    ("copy", desc),
                    Symbol: "\uE968"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // "range" 서브커맨드
        if (q.StartsWith("range ", StringComparison.OrdinalIgnoreCase))
        {
            var rangeStr = q[6..].Trim();
            items.AddRange(BuildRangeItems(rangeStr));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 서브넷 마스크 입력 (예: 255.255.255.0)
        if (TryParseMask(q, out var cidrFromMask))
        {
            items.Add(new LauncherItem(
                $"CIDR: /{cidrFromMask}",
                $"서브넷 마스크 {q} = /{cidrFromMask}",
                null,
                ("copy", $"/{cidrFromMask}"),
                Symbol: "\uE968"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // CIDR 파싱: "IP/prefix" 또는 "IP prefix"
        if (!TryParseCidr(q, out var ip, out var prefix))
        {
            items.Add(new LauncherItem("형식 오류", "예: 192.168.1.0/24", null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

    // ── 서브넷 계산 ──────────────────────────────────────────────────────────

    private static IEnumerable<LauncherItem> BuildSubnetItems(uint ip, int prefix)
    {
        var mask        = prefix == 0 ? 0u : ~0u << (32 - prefix);
        var network     = ip & mask;
        var broadcast   = network | ~mask;
        var firstHost   = prefix < 31 ? network + 1 : network;
        var lastHost    = prefix < 31 ? broadcast - 1 : broadcast;
        var hostCount   = prefix >= 31 ? (1u << (32 - prefix))
                                       : (broadcast - network - 1);

        var summaryText = $"""
            네트워크:   {ToIp(network)}/{prefix}
            서브넷마스크: {ToIp(mask)}
            첫 호스트:  {ToIp(firstHost)}
            마지막 호스트: {ToIp(lastHost)}
            브로드캐스트: {ToIp(broadcast)}
            사용 가능 호스트: {hostCount:N0}개
            """;

        yield return new LauncherItem(
            $"{ToIp(network)}/{prefix}",
            $"호스트 {hostCount:N0}개  ·  전체 복사: Enter",
            null,
            ("copy", summaryText),
            Symbol: "\uE968");

        yield return new LauncherItem("서브넷 마스크", ToIp(mask),   null, ("copy", ToIp(mask)),      Symbol: "\uE968");
        yield return new LauncherItem("네트워크 주소", ToIp(network), null, ("copy", ToIp(network)),   Symbol: "\uE968");
        yield return new LauncherItem("브로드캐스트",  ToIp(broadcast), null, ("copy", ToIp(broadcast)), Symbol: "\uE968");
        yield return new LauncherItem("첫 호스트",    ToIp(firstHost), null, ("copy", ToIp(firstHost)), Symbol: "\uE968");
        yield return new LauncherItem("마지막 호스트", ToIp(lastHost),  null, ("copy", ToIp(lastHost)),  Symbol: "\uE968");
        yield return new LauncherItem(
            "사용 가능 호스트",
            $"{hostCount:N0}개",
            null,
            ("copy", hostCount.ToString()),
            Symbol: "\uE968");

        // 입력 IP가 이 서브넷에 속하는지 확인
        if ((ip & mask) == network && ip != network && ip != broadcast)
        {
            yield return new LauncherItem(
                $"입력 IP {ToIp(ip)}",
                "이 서브넷에 속함 ✓",
                null, null, Symbol: "\uE73E");
        }

        // 이진 표현
        yield return new LauncherItem(
            "이진 마스크",
            ToBinary(mask),
            null,
            ("copy", ToBinary(mask)),
            Symbol: "\uE8C4");
    }

    private static IEnumerable<LauncherItem> BuildRangeItems(string rangeStr)
    {
        // "192.168.1.10-50" 또는 "192.168.1.10-192.168.1.50"
        var parts = rangeStr.Split('-', 2);
        if (parts.Length != 2)
        {
            yield return new LauncherItem("형식 오류", "예: 192.168.1.10-50", null, null, Symbol: "\uE783");
            yield break;
        }

        if (!TryParseIp(parts[0].Trim(), out var startIp))
        {
            yield return new LauncherItem("IP 형식 오류", parts[0], null, null, Symbol: "\uE783");
            yield break;
        }

        uint endIp;
        if (uint.TryParse(parts[1].Trim(), out var lastOctet))
        {
            endIp = (startIp & 0xFFFFFF00) | lastOctet;
        }
        else if (!TryParseIp(parts[1].Trim(), out endIp))
        {
            yield return new LauncherItem("IP 형식 오류", parts[1], null, null, Symbol: "\uE783");
            yield break;
        }

        if (endIp < startIp)
        {
            yield return new LauncherItem("오류", "끝 IP가 시작 IP보다 작습니다", null, null, Symbol: "\uE783");
            yield break;
        }

        var count = endIp - startIp + 1;
        yield return new LauncherItem(
            $"{ToIp(startIp)} — {ToIp(endIp)}",
            $"{count}개 IP",
            null,
            ("copy", $"{ToIp(startIp)} - {ToIp(endIp)} ({count}개)"),
            Symbol: "\uE968");

        yield return new LauncherItem("시작 IP", ToIp(startIp), null, ("copy", ToIp(startIp)), Symbol: "\uE968");
        yield return new LauncherItem("끝 IP",   ToIp(endIp),   null, ("copy", ToIp(endIp)),   Symbol: "\uE968");
        yield return new LauncherItem("IP 수",   $"{count:N0}개", null, ("copy", count.ToString()), Symbol: "\uE968");
    }

    // ── 파싱 헬퍼 ────────────────────────────────────────────────────────────

    private static bool TryParseCidr(string s, out uint ip, out int prefix)
    {
        ip = 0; prefix = 24;

        // "IP/prefix" 형식
        var slashIdx = s.IndexOf('/');
        if (slashIdx >= 0)
        {
            if (!TryParseIp(s[..slashIdx].Trim(), out ip)) return false;
            if (!int.TryParse(s[(slashIdx + 1)..].Trim(), out prefix)) return false;
            prefix = Math.Clamp(prefix, 0, 32);
            return true;
        }

        // "IP prefix" 형식 (공백 구분)
        var spaceIdx = s.LastIndexOf(' ');
        if (spaceIdx >= 0 && int.TryParse(s[(spaceIdx + 1)..].Trim(), out var p))
        {
            prefix = Math.Clamp(p, 0, 32);
            return TryParseIp(s[..spaceIdx].Trim(), out ip);
        }

        // IP만 입력 → /24 기본
        return TryParseIp(s.Trim(), out ip);
    }

    private static bool TryParseIp(string s, out uint ip)
    {
        ip = 0;
        if (!IPAddress.TryParse(s, out var addr)) return false;
        if (addr.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) return false;
        var bytes = addr.GetAddressBytes();
        ip = ((uint)bytes[0] << 24) | ((uint)bytes[1] << 16)
           | ((uint)bytes[2] << 8)  |  (uint)bytes[3];
        return true;
    }

    private static bool TryParseMask(string s, out int prefix)
    {
        prefix = 0;
        if (!TryParseIp(s, out var mask)) return false;
        if (mask == 0) { prefix = 0; return true; }

        // 유효한 서브넷 마스크인지 확인 (연속된 1 뒤에 0)
        var inverted = ~mask;
        if ((inverted & (inverted + 1)) != 0) return false;

        prefix = 0;
        var tmp = mask;
        while ((tmp & 0x80000000) != 0) { prefix++; tmp <<= 1; }
        return true;
    }

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

    private static string ToBinary(uint val) =>
        $"{Convert.ToString((int)(val >> 16), 2).PadLeft(16, '0')} {Convert.ToString((int)(val & 0xFFFF), 2).PadLeft(16, '0')}";
}
