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

namespace AxCopilot.Handlers;

/// <summary>
/// L11-2: JWT 토큰 디코더 핸들러. "jwt" 프리픽스로 사용합니다.
///
/// 예: jwt                         → 클립보드의 JWT 자동 분석
///     jwt eyJhbGci...             → 토큰 직접 입력
///     jwt header                  → 클립보드 JWT 헤더만 표시
///     jwt payload                 → 클립보드 JWT 페이로드만 표시
/// Enter → 결과를 클립보드에 복사.
/// 주의: 서명(signature) 검증은 수행하지 않음 — 분석 전용.
/// </summary>
public class JwtHandler : IActionHandler
{
    public string? Prefix => "jwt";

    public PluginMetadata Metadata => new(
        "JWT",
        "JWT 토큰 디코더 — 헤더 · 페이로드 · 만료일 분석",
        "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))
        {
            var clip = GetClipboard();
            if (LooksJwt(clip))
            {
                items.AddRange(DecodeJwt(clip, "all"));
            }
            else
            {
                items.Add(new LauncherItem("JWT 디코더",
                    "JWT 토큰을 클립보드에 복사하거나 직접 입력하세요",
                    null, null, Symbol: "\uE72E"));
                items.Add(new LauncherItem("jwt eyJ…",      "토큰 직접 입력", null, null, Symbol: "\uE72E"));
                items.Add(new LauncherItem("jwt header",    "헤더만 표시",    null, null, Symbol: "\uE72E"));
                items.Add(new LauncherItem("jwt payload",   "페이로드만 표시", null, null, Symbol: "\uE72E"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        switch (sub)
        {
            case "header":
            {
                var src = GetTokenSource(parts);
                items.AddRange(DecodeJwt(src, "header"));
                break;
            }
            case "payload":
            case "claims":
            case "body":
            {
                var src = GetTokenSource(parts);
                items.AddRange(DecodeJwt(src, "payload"));
                break;
            }
            default:
            {
                // 토큰 자체 입력 (eyJ로 시작)
                var token = LooksJwt(q) ? q : GetClipboard();
                if (!LooksJwt(token))
                {
                    items.Add(new LauncherItem("JWT 형식 아님",
                        "eyJ…로 시작하는 JWT 토큰을 입력하거나 클립보드에 복사하세요",
                        null, null, Symbol: "\uE783"));
                }
                else
                {
                    items.AddRange(DecodeJwt(token, "all"));
                }
                break;
            }
        }

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

    // ── JWT 디코딩 ────────────────────────────────────────────────────────────

    private static IEnumerable<LauncherItem> DecodeJwt(string token, string mode)
    {
        if (!LooksJwt(token))
        {
            yield return new LauncherItem("JWT 없음",
                "클립보드에 eyJ…로 시작하는 JWT가 없습니다", null, null, Symbol: "\uE783");
            yield break;
        }

        var parts = token.Split('.');
        if (parts.Length < 2)
        {
            yield return new LauncherItem("형식 오류", "JWT는 최소 2개의 점(.)으로 구성됩니다", null, null, Symbol: "\uE783");
            yield break;
        }

        // 헤더 디코딩
        if (mode is "header" or "all")
        {
            var headerJson = TryDecodeBase64Url(parts[0]);
            if (headerJson != null)
            {
                var pretty = TryPrettyJson(headerJson) ?? headerJson;
                yield return new LauncherItem("─ 헤더 ─", "", null, null, Symbol: "\uE72E");
                foreach (var item in ExtractJsonFields(headerJson, "헤더"))
                    yield return item;
                yield return new LauncherItem("헤더 JSON", "전체 복사", null, ("copy", pretty), Symbol: "\uE72E");
            }
        }

        // 페이로드 디코딩
        if (mode is "payload" or "all")
        {
            if (parts.Length < 2)
            {
                yield return new LauncherItem("페이로드 없음", "JWT에 페이로드가 없습니다", null, null, Symbol: "\uE783");
                yield break;
            }

            var payloadJson = TryDecodeBase64Url(parts[1]);
            if (payloadJson != null)
            {
                yield return new LauncherItem("─ 페이로드 ─", "", null, null, Symbol: "\uE72E");

                // 만료일(exp) 특별 처리
                var expItem = ExtractExpiry(payloadJson);
                if (expItem != null) yield return expItem;

                foreach (var item in ExtractJsonFields(payloadJson, "페이로드"))
                    yield return item;

                var pretty = TryPrettyJson(payloadJson) ?? payloadJson;
                yield return new LauncherItem("페이로드 JSON", "전체 복사", null, ("copy", pretty), Symbol: "\uE72E");
            }
        }

        // 서명 유무
        if (mode == "all")
        {
            var hasSig = parts.Length >= 3 && !string.IsNullOrEmpty(parts[2]);
            yield return new LauncherItem(
                "서명",
                hasSig ? "있음 (검증 미지원 — 분석 전용)" : "없음 (alg:none)",
                null, null, Symbol: "\uE72E");
        }
    }

    private static IEnumerable<LauncherItem> ExtractJsonFields(string json, string section)
    {
        JsonDocument doc;
        try { doc = JsonDocument.Parse(json); }
        catch { yield break; }

        using (doc)
        {
            foreach (var prop in doc.RootElement.EnumerateObject())
            {
                var val = prop.Value.ValueKind switch
                {
                    JsonValueKind.String => prop.Value.GetString() ?? "",
                    JsonValueKind.Number => prop.Value.GetRawText(),
                    JsonValueKind.True   => "true",
                    JsonValueKind.False  => "false",
                    JsonValueKind.Null   => "null",
                    _                   => prop.Value.GetRawText(),
                };

                // exp, iat, nbf 는 타임스탬프 → 날짜 변환해서 별도 표시
                if (prop.Name is "exp" or "iat" or "nbf")
                {
                    if (long.TryParse(val, out var ts))
                    {
                        var dt     = DateTimeOffset.FromUnixTimeSeconds(ts).ToLocalTime();
                        var label  = prop.Name switch { "exp" => "만료(exp)", "iat" => "발급(iat)", _ => "유효 시작(nbf)" };
                        yield return new LauncherItem(label, dt.ToString("yyyy-MM-dd HH:mm:ss"), null, ("copy", dt.ToString("o")), Symbol: "\uE72E");
                        continue;
                    }
                }

                var display = val.Length > 60 ? val[..60] + "…" : val;
                yield return new LauncherItem(prop.Name, display, null, ("copy", val), Symbol: "\uE72E");
            }
        }
    }

    private static LauncherItem? ExtractExpiry(string payloadJson)
    {
        try
        {
            var doc = JsonDocument.Parse(payloadJson);
            if (!doc.RootElement.TryGetProperty("exp", out var expProp)) return null;
            if (!expProp.TryGetInt64(out var exp)) return null;

            var now    = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            var dt     = DateTimeOffset.FromUnixTimeSeconds(exp).ToLocalTime();
            var remain = exp - now;

            string status;
            if (remain < 0)
                status = $"만료됨 ({Math.Abs(remain / 60)}분 전)";
            else if (remain < 60)
                status = $"곧 만료 ({remain}초 남음)";
            else if (remain < 3600)
                status = $"유효 ({remain / 60}분 남음)";
            else if (remain < 86400)
                status = $"유효 ({remain / 3600}시간 남음)";
            else
                status = $"유효 ({remain / 86400}일 남음)";

            return new LauncherItem(
                $"만료 상태: {status}",
                dt.ToString("yyyy-MM-dd HH:mm:ss"),
                null, null, Symbol: remain < 0 ? "\uE783" : "\uE73E");
        }
        catch { return null; }
    }

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

    private static string? TryDecodeBase64Url(string input)
    {
        try
        {
            // Base64Url → Base64
            var base64 = input.Replace('-', '+').Replace('_', '/');
            var pad    = (4 - base64.Length % 4) % 4;
            base64 += new string('=', pad);
            var bytes = Convert.FromBase64String(base64);
            return Encoding.UTF8.GetString(bytes);
        }
        catch { return null; }
    }

    private static string? TryPrettyJson(string json)
    {
        try
        {
            var doc = JsonDocument.Parse(json);
            return JsonSerializer.Serialize(doc.RootElement,
                new JsonSerializerOptions { WriteIndented = true });
        }
        catch { return null; }
    }

    private static bool LooksJwt(string? s)
    {
        if (string.IsNullOrWhiteSpace(s)) return false;
        s = s.Trim();
        return s.StartsWith("eyJ", StringComparison.Ordinal) && s.Contains('.');
    }

    private static string GetTokenSource(string[] parts)
    {
        // parts[1]에 토큰이 있으면 사용, 없으면 클립보드
        if (parts.Length > 1 && LooksJwt(parts[1])) return parts[1];
        return GetClipboard();
    }

    private static string GetClipboard()
    {
        try
        {
            return System.Windows.Application.Current.Dispatcher.Invoke(
                () => Clipboard.ContainsText() ? Clipboard.GetText().Trim() : "");
        }
        catch { return ""; }
    }
}
