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

namespace AxCopilot.Handlers;

/// <summary>
/// L7-4: 네트워크 진단 핸들러. "net" 프리픽스로 사용합니다.
/// (기존 PortHandler의 포트/프로세스 조회와 구분되는 어댑터·핑·DNS 진단 기능)
///
/// 예: net                  → 활성 네트워크 어댑터 IP 목록
///     net ping 8.8.8.8     → 핑 테스트 (사외 모드에서만 외부 호스트)
///     net ping localhost    → 로컬 핑 (항상 허용)
///     net dns google.com   → DNS A 레코드 조회 (사외 모드에서만 외부)
///     net ip               → 로컬 IP 정보 (공인 IP는 사외 모드에서만 표시)
///     net adapter          → 네트워크 어댑터 전체 정보
/// Enter → 결과를 클립보드에 복사.
/// </summary>
public class NetDiagHandler : IActionHandler
{
    public string? Prefix => "net";

    public PluginMetadata Metadata => new(
        "NetDiag",
        "네트워크 진단 — IP · ping · DNS 조회 · 어댑터 상태",
        "1.0",
        "AX");

    public async Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim().ToLowerInvariant();
        var items = new List<LauncherItem>();

        if (string.IsNullOrWhiteSpace(q))
        {
            // 로컬 어댑터 IP 빠른 표시
            var adapters = GetLocalAdapters();
            foreach (var (name, ip, mac) in adapters)
            {
                items.Add(new LauncherItem(
                    ip,
                    $"{name}  ·  MAC: {mac}",
                    null,
                    ("copy", ip),
                    Symbol: "\uE968"));
            }

            // 서브커맨드 안내
            items.Add(new LauncherItem("net ping <호스트>",   "핑 테스트",                    null, null, Symbol: "\uE8F2"));
            items.Add(new LauncherItem("net dns <도메인>",    "DNS A 레코드 조회",            null, null, Symbol: "\uE968"));
            items.Add(new LauncherItem("net ip",              "로컬 IP 전체 정보",            null, ("ip_info", ""), Symbol: "\uE968"));
            items.Add(new LauncherItem("net adapter",         "어댑터 세부 정보",             null, ("adapter_info", ""), Symbol: "\uE968"));
            return items;
        }

        // ── ping ──────────────────────────────────────────────────────────
        if (q.StartsWith("ping "))
        {
            var host = query.Trim()["ping ".Length..].Trim();
            if (string.IsNullOrEmpty(host))
            {
                items.Add(new LauncherItem("호스트를 입력하세요", "예: net ping 192.168.1.1", null, null, Symbol: "\uE783"));
                return items;
            }

            // 사내 모드에서는 내부 호스트만 허용
            if (!IsAllowedInInternalMode(host))
            {
                items.Add(new LauncherItem(
                    "사내 모드 — 외부 호스트 차단",
                    "사외 모드에서만 외부 IP/도메인에 핑 가능합니다",
                    null, null, Symbol: "\uE785"));
                return items;
            }

            items.Add(new LauncherItem(
                $"Ping: {host}",
                "테스트 중...",
                null,
                ("ping", host),
                Symbol: "\uE8F2"));

            // 비동기 핑 시도
            try
            {
                var result = await PingAsync(host, ct);
                items.Clear();
                foreach (var r in result)
                    items.Add(r);
            }
            catch (OperationCanceledException) { }

            return items;
        }

        // ── dns ──────────────────────────────────────────────────────────
        if (q.StartsWith("dns "))
        {
            var domain = query.Trim()["dns ".Length..].Trim();
            if (string.IsNullOrEmpty(domain))
            {
                items.Add(new LauncherItem("도메인을 입력하세요", "예: net dns example.com", null, null, Symbol: "\uE783"));
                return items;
            }

            if (!IsAllowedInInternalMode(domain))
            {
                items.Add(new LauncherItem(
                    "사내 모드 — 외부 도메인 차단",
                    "사외 모드에서만 외부 도메인 DNS 조회 가능합니다",
                    null, null, Symbol: "\uE785"));
                return items;
            }

            try
            {
                var ips = await Dns.GetHostAddressesAsync(domain, ct);
                if (ips.Length == 0)
                {
                    items.Add(new LauncherItem($"{domain}", "DNS 조회 결과 없음", null, null, Symbol: "\uE783"));
                }
                else
                {
                    items.Add(new LauncherItem(
                        $"{domain} — {ips.Length}개 레코드",
                        string.Join(", ", ips.Select(ip => ip.ToString())),
                        null,
                        ("copy", string.Join("\n", ips.Select(ip => ip.ToString()))),
                        Symbol: "\uE968"));

                    foreach (var ip in ips)
                    {
                        items.Add(new LauncherItem(
                            ip.ToString(),
                            ip.AddressFamily == AddressFamily.InterNetworkV6 ? "IPv6" : "IPv4",
                            null,
                            ("copy", ip.ToString()),
                            Symbol: "\uE968"));
                    }
                }
            }
            catch (Exception ex)
            {
                items.Add(new LauncherItem("DNS 조회 실패", ex.Message, null, null, Symbol: "\uE783"));
            }

            return items;
        }

        // ── ip ────────────────────────────────────────────────────────────
        if (q == "ip")
        {
            items.Add(new LauncherItem("로컬 IP 정보", "어댑터별 IP 주소", null, ("ip_info", ""), Symbol: "\uE968"));
            var adapters = GetLocalAdapters();
            foreach (var (name, ip, mac) in adapters)
            {
                items.Add(new LauncherItem(
                    ip,
                    $"{name}  ·  MAC: {mac}",
                    null,
                    ("copy", ip),
                    Symbol: "\uE968"));
            }
            return items;
        }

        // ── adapter ──────────────────────────────────────────────────────
        if (q.StartsWith("adapter"))
        {
            items.Add(new LauncherItem(
                "어댑터 전체 정보 (클립보드 복사)",
                "활성 어댑터, IP, MAC, 속도",
                null,
                ("adapter_info", ""),
                Symbol: "\uE968"));
            var adapters = GetLocalAdapters();
            foreach (var (name, ip, mac) in adapters)
            {
                items.Add(new LauncherItem(
                    name,
                    $"IP: {ip}  ·  MAC: {mac}",
                    null,
                    ("copy", $"{name}: {ip} ({mac})"),
                    Symbol: "\uE968"));
            }
            return items;
        }

        // 미인식 → 기본 표시
        var defaultAdapters = GetLocalAdapters();
        foreach (var (name, ip, mac) in defaultAdapters)
        {
            items.Add(new LauncherItem(ip, $"{name}  ·  MAC: {mac}", null, ("copy", ip), Symbol: "\uE968"));
        }
        return items;
    }

    public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        switch (item.Data)
        {
            case ("copy", string text):
                TryCopyToClipboard(text);
                NotificationService.Notify("NetDiag", "클립보드에 복사했습니다.");
                break;

            case ("ping", string host):
                var pingItems = await PingAsync(host, ct);
                var summary   = string.Join("\n", pingItems.Select(i => $"{i.Title}  {i.Subtitle}"));
                TryCopyToClipboard(summary);
                NotificationService.Notify("Ping", pingItems.FirstOrDefault()?.Title ?? host);
                break;

            case ("ip_info", _):
            case ("adapter_info", _):
                var adapterInfo = BuildAdapterInfoText();
                TryCopyToClipboard(adapterInfo);
                NotificationService.Notify("NetDiag", "어댑터 정보를 클립보드에 복사했습니다.");
                break;
        }
    }

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

    private static List<(string Name, string IP, string MAC)> GetLocalAdapters()
    {
        var result = new List<(string, string, string)>();
        try
        {
            foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces())
            {
                if (adapter.OperationalStatus != OperationalStatus.Up) continue;
                if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;

                var props = adapter.GetIPProperties();
                foreach (var addr in props.UnicastAddresses)
                {
                    if (addr.Address.AddressFamily != AddressFamily.InterNetwork) continue;
                    var mac = BitConverter.ToString(adapter.GetPhysicalAddress().GetAddressBytes())
                                         .Replace('-', ':');
                    result.Add((adapter.Name, addr.Address.ToString(), mac));
                }
            }
        }
        catch { /* 비핵심 */ }
        return result;
    }

    private static string BuildAdapterInfoText()
    {
        var sb = new StringBuilder();
        try
        {
            foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces())
            {
                if (adapter.OperationalStatus != OperationalStatus.Up) continue;
                if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;

                var props = adapter.GetIPProperties();
                var mac   = BitConverter.ToString(adapter.GetPhysicalAddress().GetAddressBytes())
                                        .Replace('-', ':');
                var speed = adapter.Speed > 0 ? $"{adapter.Speed / 1_000_000} Mbps" : "-";

                sb.AppendLine($"[{adapter.Name}]");
                sb.AppendLine($"  속도: {speed}");
                sb.AppendLine($"  MAC:  {mac}");
                foreach (var addr in props.UnicastAddresses)
                    sb.AppendLine($"  IP:   {addr.Address} / {addr.PrefixLength}");
                foreach (var gw in props.GatewayAddresses)
                    sb.AppendLine($"  GW:   {gw.Address}");
                sb.AppendLine();
            }
        }
        catch { /* 비핵심 */ }
        return sb.ToString().Trim();
    }

    private static async Task<List<LauncherItem>> PingAsync(string host, CancellationToken ct)
    {
        var items = new List<LauncherItem>();
        try
        {
            using var pinger = new Ping();
            var results = new List<(bool Ok, long Ms, string Status)>();

            for (int i = 0; i < 4; i++)
            {
                ct.ThrowIfCancellationRequested();
                try
                {
                    var reply = await pinger.SendPingAsync(host, 1500);
                    results.Add((reply.Status == IPStatus.Success,
                                 reply.RoundtripTime,
                                 reply.Status.ToString()));
                }
                catch
                {
                    results.Add((false, -1, "Timeout"));
                }
                if (i < 3) await Task.Delay(200, ct);
            }

            var successCount = results.Count(r => r.Ok);
            var avgMs        = results.Where(r => r.Ok).Select(r => r.Ms).DefaultIfEmpty(0)
                                      .Average();
            var loss         = (4 - successCount) * 25;

            items.Add(new LauncherItem(
                $"Ping {host}  {avgMs:F0}ms",
                $"패킷 손실: {loss}%  ({successCount}/4 성공)",
                null,
                ("copy", $"Ping {host}: {avgMs:F0}ms, 손실 {loss}%"),
                Symbol: successCount == 4 ? "\uE73E" : successCount == 0 ? "\uE783" : "\uE7BA"));

            for (int i = 0; i < results.Count; i++)
            {
                var (ok, ms, status) = results[i];
                items.Add(new LauncherItem(
                    ok ? $"응답  {ms}ms" : "시간 초과",
                    $"#{i + 1}  {status}",
                    null,
                    ("copy", ok ? $"{ms}ms" : "timeout"),
                    Symbol: ok ? "\uE73E" : "\uE783"));
            }
        }
        catch (OperationCanceledException)
        {
            throw;
        }
        catch (Exception ex)
        {
            items.Add(new LauncherItem("핑 실패", ex.Message, null, null, Symbol: "\uE783"));
        }
        return items;
    }

    /// <summary>사내 모드에서 외부 호스트 차단 여부 확인.</summary>
    private static bool IsAllowedInInternalMode(string host)
    {
        var settings = (System.Windows.Application.Current as App)?.SettingsService?.Settings;
        var isInternal = settings?.InternalModeEnabled ?? true;
        if (!isInternal) return true; // 사외 모드: 모두 허용

        // 사내 모드: 내부 주소만 허용
        return host.Equals("localhost", StringComparison.OrdinalIgnoreCase)
            || host.StartsWith("127.", StringComparison.Ordinal)
            || host.StartsWith("192.168.", StringComparison.Ordinal)
            || host.StartsWith("10.", StringComparison.Ordinal)
            || host.StartsWith("172.", StringComparison.Ordinal);
    }

    private static void TryCopyToClipboard(string text)
    {
        try
        {
            System.Windows.Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(text));
        }
        catch { /* 비핵심 */ }
    }
}
