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

namespace AxCopilot.Handlers;

/// <summary>
/// L10-2: UUID/GUID 생성기 핸들러. "uuid" 프리픽스로 사용합니다.
///
/// 예: uuid              → UUID v4 1개 생성
///     uuid 5            → UUID v4 5개 생성
///     uuid upper        → 대문자 UUID 생성
///     uuid v4           → UUID v4 (랜덤)
///     uuid seq          → 순차 UUID (시간 기반, 정렬 가능)
///     uuid short        → 짧은 고유 ID (8자리 hex)
///     uuid nil          → Nil UUID (00000000-…)
///     uuid parse <uuid> → UUID 분석 (버전, 타임스탬프 등)
/// Enter → 결과를 클립보드에 복사.
/// </summary>
public class UuidHandler : IActionHandler
{
    public string? Prefix => "uuid";

    public PluginMetadata Metadata => new(
        "UUID",
        "UUID/GUID 생성기 — v4 · 순차 · 짧은 ID · 분석",
        "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))
        {
            // 기본: v4 1개 생성
            var uuid = Guid.NewGuid().ToString();
            items.Add(new LauncherItem(
                uuid,
                "UUID v4 (랜덤)  · Enter 복사",
                null,
                ("copy", uuid),
                Symbol: "\uF0E2"));

            items.Add(new LauncherItem("uuid 5",     "5개 생성",          null, null, Symbol: "\uF0E2"));
            items.Add(new LauncherItem("uuid upper",  "대문자 UUID",       null, null, Symbol: "\uF0E2"));
            items.Add(new LauncherItem("uuid seq",    "순차 UUID (정렬가능)", null, null, Symbol: "\uF0E2"));
            items.Add(new LauncherItem("uuid short",  "짧은 ID (8자리)",   null, null, Symbol: "\uF0E2"));
            items.Add(new LauncherItem("uuid parse …", "UUID 분석",       null, null, Symbol: "\uF0E2"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        // "uuid parse <value>"
        if (sub == "parse" && parts.Length >= 2)
        {
            items.AddRange(ParseUuid(string.Join(" ", parts.Skip(1))));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // "uuid nil"
        if (sub == "nil")
        {
            var nil = "00000000-0000-0000-0000-000000000000";
            items.Add(new LauncherItem(nil, "Nil UUID", null, ("copy", nil), Symbol: "\uF0E2"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // "uuid seq"
        if (sub == "seq")
        {
            items.AddRange(GenerateSequential(5));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // "uuid short"
        if (sub == "short")
        {
            items.AddRange(GenerateShort(5));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // "uuid upper"
        if (sub == "upper")
        {
            var upper = Guid.NewGuid().ToString().ToUpperInvariant();
            items.Add(new LauncherItem(upper, "대문자 UUID v4  · Enter 복사", null, ("copy", upper), Symbol: "\uF0E2"));
            for (var i = 0; i < 4; i++)
            {
                var u = Guid.NewGuid().ToString().ToUpperInvariant();
                items.Add(new LauncherItem(u, "대문자 UUID v4", null, ("copy", u), Symbol: "\uF0E2"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // "uuid v4"
        if (sub == "v4")
        {
            items.AddRange(GenerateV4(5));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // "uuid <숫자>" — N개 생성
        if (int.TryParse(sub, out var count))
        {
            count = Math.Clamp(count, 1, 20);
            items.AddRange(GenerateV4(count));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // UUID 자체를 입력한 경우 → parse
        if (Guid.TryParse(q, out _))
        {
            items.AddRange(ParseUuid(q));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        items.Add(new LauncherItem("알 수 없는 명령",
            "예: uuid / uuid 5 / uuid upper / uuid seq / uuid short / uuid parse",
            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("UUID", "클립보드에 복사했습니다.");
            }
            catch { /* 비핵심 */ }
        }
        return Task.CompletedTask;
    }

    // ── 생성 헬퍼 ─────────────────────────────────────────────────────────────

    private static IEnumerable<LauncherItem> GenerateV4(int count)
    {
        var all = new List<string>();
        for (var i = 0; i < count; i++)
        {
            var uuid = Guid.NewGuid().ToString();
            all.Add(uuid);
        }

        if (count > 1)
        {
            yield return new LauncherItem(
                $"UUID v4  {count}개",
                "전체 복사: Enter",
                null,
                ("copy", string.Join("\n", all)),
                Symbol: "\uF0E2");
        }

        foreach (var uuid in all)
            yield return new LauncherItem(uuid, "UUID v4  · Enter 복사", null, ("copy", uuid), Symbol: "\uF0E2");
    }

    /// <summary>
    /// 시간 기반 순차 UUID (UUIDv7 스타일: 밀리초 타임스탬프 + 랜덤 비트).
    /// 정렬 가능하고 시간 정보 포함.
    /// </summary>
    private static IEnumerable<LauncherItem> GenerateSequential(int count)
    {
        var all = new List<string>();
        for (var i = 0; i < count; i++)
        {
            if (i > 0) System.Threading.Thread.Sleep(1); // 밀리초 차이 보장
            var uuid = NewSequentialGuid();
            all.Add(uuid);
        }

        yield return new LauncherItem(
            $"순차 UUID  {count}개",
            "시간 기반 정렬 가능 · 전체 복사: Enter",
            null,
            ("copy", string.Join("\n", all)),
            Symbol: "\uF0E2");

        foreach (var uuid in all)
            yield return new LauncherItem(uuid, "순차 UUID  · Enter 복사", null, ("copy", uuid), Symbol: "\uF0E2");
    }

    private static IEnumerable<LauncherItem> GenerateShort(int count)
    {
        var all = new List<string>();
        for (var i = 0; i < count; i++)
            all.Add(NewShortId());

        yield return new LauncherItem(
            $"짧은 ID  {count}개",
            "8자리 hex · 전체 복사: Enter",
            null,
            ("copy", string.Join("\n", all)),
            Symbol: "\uF0E2");

        foreach (var id in all)
            yield return new LauncherItem(id, "짧은 ID (8자리)  · Enter 복사", null, ("copy", id), Symbol: "\uF0E2");
    }

    private static IEnumerable<LauncherItem> ParseUuid(string raw)
    {
        raw = raw.Trim();
        if (!Guid.TryParse(raw, out var guid))
        {
            yield return new LauncherItem("UUID 파싱 실패", $"'{raw}'은 유효한 UUID가 아닙니다", null, null, Symbol: "\uE783");
            yield break;
        }

        var bytes   = guid.ToByteArray();
        var version = (bytes[7] >> 4) & 0x0F;
        var variant = (bytes[8] >> 6) & 0x03;

        yield return new LauncherItem(
            guid.ToString(),
            $"버전 {version}  ·  변형 {variant}",
            null,
            ("copy", guid.ToString()),
            Symbol: "\uF0E2");

        yield return new LauncherItem("소문자", guid.ToString(),            null, ("copy", guid.ToString()),            Symbol: "\uF0E2");
        yield return new LauncherItem("대문자", guid.ToString().ToUpper(),  null, ("copy", guid.ToString().ToUpper()),  Symbol: "\uF0E2");
        yield return new LauncherItem("중괄호", $"{{{guid}}}",              null, ("copy", $"{{{guid}}}"),              Symbol: "\uF0E2");
        yield return new LauncherItem("대시 없음", guid.ToString("N"),      null, ("copy", guid.ToString("N")),         Symbol: "\uF0E2");
        yield return new LauncherItem("버전",  $"UUID v{version}",          null, null, Symbol: "\uF0E2");
        yield return new LauncherItem("변형",  variant == 2 ? "RFC 4122" : variant == 3 ? "Microsoft" : $"변형 {variant}", null, null, Symbol: "\uF0E2");

        // 시간 기반 버전 (v1)이면 타임스탬프 복원 시도
        if (version == 1)
        {
            var ts = ExtractV1Timestamp(bytes);
            if (ts.HasValue)
                yield return new LauncherItem("타임스탬프", ts.Value.ToString("yyyy-MM-dd HH:mm:ss.fff UTC"), null, null, Symbol: "\uF0E2");
        }
    }

    // ── UUID 생성 구현 ────────────────────────────────────────────────────────

    /// <summary>UUIDv7 스타일 순차 GUID: 상위 48비트 = Unix ms 타임스탬프, 하위 = 랜덤</summary>
    private static string NewSequentialGuid()
    {
        var ms     = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        var rand   = RandomNumberGenerator.GetBytes(10);
        var bytes  = new byte[16];

        // 상위 6바이트 = 타임스탬프 (big-endian ms)
        bytes[0] = (byte)(ms >> 40);
        bytes[1] = (byte)(ms >> 32);
        bytes[2] = (byte)(ms >> 24);
        bytes[3] = (byte)(ms >> 16);
        bytes[4] = (byte)(ms >> 8);
        bytes[5] = (byte)(ms);

        // 버전 비트 (v7 = 0111)
        bytes[6] = (byte)((rand[0] & 0x0F) | 0x70);
        bytes[7] = rand[1];

        // 변형 비트 (RFC 4122 = 10xx)
        bytes[8] = (byte)((rand[2] & 0x3F) | 0x80);
        bytes[9] = rand[3];

        // 나머지 랜덤
        Array.Copy(rand, 4, bytes, 10, 6);

        return new Guid(bytes).ToString();
    }

    private static string NewShortId()
    {
        var bytes = RandomNumberGenerator.GetBytes(4);
        return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
    }

    private static DateTime? ExtractV1Timestamp(byte[] bytes)
    {
        try
        {
            // UUID v1: time_low(4) + time_mid(2) + time_hi_version(2)
            var timeLow  = (long)((uint)((bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]));
            var timeMid  = (long)((ushort)((bytes[5] << 8) | bytes[4]));
            var timeHigh = (long)((ushort)((bytes[7] << 8) | bytes[6]) & 0x0FFF);
            var ticks    = (timeHigh << 48) | (timeMid << 32) | timeLow;
            // UUID epoch = Oct 15, 1582
            var uuidEpoch = new DateTime(1582, 10, 15, 0, 0, 0, DateTimeKind.Utc);
            return uuidEpoch.AddTicks(ticks);
        }
        catch { return null; }
    }
}
