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

namespace AxCopilot.Handlers;

/// <summary>
/// L20-2: 랜덤 생성기 핸들러. "rand" 프리픽스로 사용합니다.
///
/// 예: rand                   → 사용법 목록
///     rand                   → 1~100 기본 난수
///     rand 50                → 1~50 난수
///     rand 10 99             → 10~99 난수
///     rand str               → 랜덤 문자열 (12자, 영숫자)
///     rand str 20            → 20자 랜덤 문자열
///     rand str 16 alpha      → 16자 알파벳만
///     rand str 8 num         → 8자 숫자만
///     rand str 12 hex        → 12자 hex 문자열
///     rand str 16 special    → 특수문자 포함
///     rand color             → 랜덤 HEX 색상
///     rand pick 항목1 항목2   → 목록에서 무작위 선택
///     rand dice              → 주사위 1d6
///     rand dice 2d6          → 2개 주사위
///     rand dice 1d20         → 20면체 주사위
///     rand coin              → 동전 던지기
///     rand shuffle a b c d   → 목록 셔플
///     rand uuid              → UUID v4
///     rand token             → 보안 토큰 (32자 hex)
///     rand pin               → 6자리 PIN
///     rand pin 4             → 4자리 PIN
/// Enter → 결과 복사.
/// </summary>
public class RandHandler : IActionHandler
{
    public string? Prefix => "rand";

    public PluginMetadata Metadata => new(
        "Rand",
        "랜덤 생성기 — 숫자·문자열·색상·주사위·UUID·토큰·PIN·셔플",
        "1.0",
        "AX");

    private static readonly Random _rng = Random.Shared;

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

        if (string.IsNullOrWhiteSpace(q))
        {
            // 기본: 1~100 난수 + 사용법
            var n = _rng.Next(1, 101);
            items.Add(new LauncherItem($"{n}",
                "1~100 랜덤 숫자  · Enter 복사", null, ("copy", n.ToString()), Symbol: "\uE8D0"));
            items.Add(new LauncherItem("사용법",
                "rand <max>  /  rand <min> <max>  /  rand str  /  rand color  /  rand dice  /  rand pick …",
                null, null, Symbol: "\uE8D0"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        switch (sub)
        {
            // rand str [length] [charset]
            case "str" or "string" or "문자열":
            {
                int len = 12;
                if (parts.Length >= 2 && int.TryParse(parts[1], out var l)) len = Math.Clamp(l, 1, 256);
                var charset = parts.Length >= 3 ? parts[2].ToLowerInvariant() : "alnum";
                if (parts.Length == 2 && !int.TryParse(parts[1], out _)) charset = parts[1].ToLowerInvariant();
                var cs = GetCharset(charset);
                if (string.IsNullOrEmpty(cs)) { items.Add(ErrorItem("charset: alpha/num/alnum/hex/special")); break; }
                var result = RandStr(len, cs);
                items.Add(new LauncherItem(result, $"{len}자 랜덤 문자열 ({charset})  · Enter 복사",
                    null, ("copy", result), Symbol: "\uE8D0"));
                // 다른 자릿수 미리 생성
                foreach (var altLen in new[] { 8, 16, 32 }.Where(x => x != len))
                    items.Add(new LauncherItem(RandStr(altLen, cs), $"{altLen}자  ({charset})",
                        null, ("copy", RandStr(altLen, cs)), Symbol: "\uE8D0"));
                break;
            }

            // rand color
            case "color" or "색상" or "colour":
            {
                for (int i = 0; i < 5; i++)
                {
                    var r = _rng.Next(256); var g = _rng.Next(256); var b = _rng.Next(256);
                    var hex = $"#{r:X2}{g:X2}{b:X2}";
                    var rgb = $"rgb({r}, {g}, {b})";
                    var hsl = RgbToHsl(r, g, b);
                    items.Add(new LauncherItem(hex, $"{rgb}  {hsl}  · Enter 복사",
                        null, ("copy", hex), Symbol: "\uE8D0"));
                }
                break;
            }

            // rand dice [NdS]
            case "dice" or "주사위":
            {
                var spec = parts.Length >= 2 ? parts[1].ToLowerInvariant() : "1d6";
                if (!TryParseDice(spec, out var dCount, out var dSides))
                { items.Add(ErrorItem("형식: 1d6 / 2d10 / 1d20")); break; }
                var rolls  = Enumerable.Range(0, dCount).Select(_ => _rng.Next(1, dSides + 1)).ToList();
                var total  = rolls.Sum();
                var detail = string.Join(" + ", rolls);
                items.Add(new LauncherItem($"{dCount}d{dSides}  →  {total}",
                    $"각 주사위: {detail}  · Enter 복사", null, ("copy", total.ToString()), Symbol: "\uE8D0"));
                if (dCount > 1)
                {
                    items.Add(CopyItem("합계",  total.ToString()));
                    items.Add(CopyItem("상세",  detail));
                    items.Add(CopyItem("최솟값", rolls.Min().ToString()));
                    items.Add(CopyItem("최댓값", rolls.Max().ToString()));
                }
                // 다시 굴리기 미리보기
                items.Add(new LauncherItem("── 다시 굴리기 (미리보기) ──", "", null, null, Symbol: "\uE8D0"));
                for (int i = 0; i < 3; i++)
                {
                    var r2 = Enumerable.Range(0, dCount).Select(_ => _rng.Next(1, dSides + 1)).ToList();
                    items.Add(new LauncherItem($"{r2.Sum()}", string.Join(" + ", r2),
                        null, ("copy", r2.Sum().ToString()), Symbol: "\uE8D0"));
                }
                break;
            }

            // rand coin
            case "coin" or "동전":
            {
                var result = _rng.Next(2) == 0 ? "앞면 (Head)" : "뒷면 (Tail)";
                items.Add(new LauncherItem(result, "동전 던지기  · Enter 복사",
                    null, ("copy", result), Symbol: "\uE8D0"));
                // 연속 5회
                items.Add(new LauncherItem("── 연속 5회 ──", "", null, null, Symbol: "\uE8D0"));
                for (int i = 0; i < 5; i++)
                    items.Add(new LauncherItem(_rng.Next(2) == 0 ? "앞면 ⬤" : "뒷면 ○",
                        $"#{i + 1}", null, null, Symbol: "\uE8D0"));
                break;
            }

            // rand pick item1 item2 ...
            case "pick" or "선택":
            {
                if (parts.Length < 2) { items.Add(ErrorItem("예: rand pick 치킨 피자 짜장면")); break; }
                var pool    = parts[1..];
                var picked  = pool[_rng.Next(pool.Length)];
                items.Add(new LauncherItem(picked,
                    $"{pool.Length}개 중 선택  · Enter 복사",
                    null, ("copy", picked), Symbol: "\uE8D0"));
                items.Add(new LauncherItem("── 다시 선택 ──", "", null, null, Symbol: "\uE8D0"));
                for (int i = 0; i < Math.Min(3, pool.Length); i++)
                    items.Add(new LauncherItem(pool[_rng.Next(pool.Length)],
                        $"후보 {i + 1}", null, null, Symbol: "\uE8D0"));
                break;
            }

            // rand shuffle item1 item2 ...
            case "shuffle" or "섞기":
            {
                if (parts.Length < 2) { items.Add(ErrorItem("예: rand shuffle a b c d e")); break; }
                var list = parts[1..].ToList();
                for (int i = list.Count - 1; i > 0; i--)
                {
                    var j = _rng.Next(i + 1);
                    (list[i], list[j]) = (list[j], list[i]);
                }
                var joined = string.Join("  ", list);
                items.Add(new LauncherItem(joined,
                    $"{list.Count}개 셔플  · Enter 복사",
                    null, ("copy", joined), Symbol: "\uE8D0"));
                items.Add(CopyItem("쉼표 구분", string.Join(", ", list)));
                for (int r = 0; r < list.Count; r++)
                    items.Add(new LauncherItem($"#{r + 1}  {list[r]}", "", null, null, Symbol: "\uE8D0"));
                break;
            }

            // rand uuid
            case "uuid":
            {
                for (int i = 0; i < 5; i++)
                {
                    var guid = Guid.NewGuid().ToString();
                    items.Add(new LauncherItem(guid, $"UUID v4  · Enter 복사",
                        null, ("copy", guid), Symbol: "\uE8D0"));
                }
                break;
            }

            // rand token
            case "token" or "secret" or "key":
            {
                int tLen = 32;
                if (parts.Length >= 2 && int.TryParse(parts[1], out var tl)) tLen = Math.Clamp(tl, 8, 128);
                var tokenBytes = new byte[tLen];
                System.Security.Cryptography.RandomNumberGenerator.Fill(tokenBytes);
                var tokenHex = BitConverter.ToString(tokenBytes).Replace("-", "").ToLowerInvariant();
                var tokenB64 = Convert.ToBase64String(tokenBytes);
                items.Add(new LauncherItem(tokenHex, $"{tLen}바이트 보안 토큰 (hex)  · Enter 복사",
                    null, ("copy", tokenHex), Symbol: "\uE8D0"));
                items.Add(CopyItem("Hex",    tokenHex));
                items.Add(CopyItem("Base64", tokenB64));
                break;
            }

            // rand pin [length]
            case "pin":
            {
                int pLen = 6;
                if (parts.Length >= 2 && int.TryParse(parts[1], out var pl)) pLen = Math.Clamp(pl, 4, 12);
                var pin = string.Concat(Enumerable.Range(0, pLen).Select(_ => _rng.Next(10)));
                items.Add(new LauncherItem(pin, $"{pLen}자리 PIN  · Enter 복사",
                    null, ("copy", pin), Symbol: "\uE8D0"));
                items.Add(new LauncherItem("── 추가 PIN ──", "", null, null, Symbol: "\uE8D0"));
                for (int i = 0; i < 4; i++)
                    items.Add(new LauncherItem(
                        string.Concat(Enumerable.Range(0, pLen).Select(_ => _rng.Next(10))),
                        $"{pLen}자리", null, null, Symbol: "\uE8D0"));
                break;
            }

            default:
            {
                // rand <max> 또는 rand <min> <max>
                if (int.TryParse(sub, out var max))
                {
                    int min = 1;
                    if (parts.Length >= 2 && int.TryParse(parts[1], out var mx)) { min = max; max = mx; }
                    if (min > max) (min, max) = (max, min);
                    var n = _rng.Next(min, max + 1);
                    items.Add(new LauncherItem($"{n}",
                        $"{min}~{max} 랜덤 숫자  · Enter 복사",
                        null, ("copy", n.ToString()), Symbol: "\uE8D0"));
                    items.Add(new LauncherItem("── 추가 결과 ──", "", null, null, Symbol: "\uE8D0"));
                    for (int i = 0; i < 4; i++)
                        items.Add(new LauncherItem(_rng.Next(min, max + 1).ToString(),
                            $"{min}~{max}", null, null, Symbol: "\uE8D0"));
                }
                else
                {
                    items.Add(new LauncherItem($"알 수 없는 서브커맨드: '{sub}'",
                        "rand / rand <n> / rand str / rand color / rand dice / rand pick / rand uuid / rand token / rand pin",
                        null, null, Symbol: "\uE783"));
                }
                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("Rand", "클립보드에 복사했습니다.");
            }
            catch { }
        }
        return Task.CompletedTask;
    }

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

    private static string GetCharset(string name) => name switch
    {
        "alpha" or "알파"    => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
        "num"  or "숫자"     => "0123456789",
        "alnum" or "영숫자"  => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
        "hex"                => "0123456789abcdef",
        "lower"              => "abcdefghijklmnopqrstuvwxyz",
        "upper"              => "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
        "special"            => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*",
        _                    => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    };

    private static string RandStr(int len, string charset)
    {
        var sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) sb.Append(charset[_rng.Next(charset.Length)]);
        return sb.ToString();
    }

    private static bool TryParseDice(string s, out int count, out int sides)
    {
        count = sides = 0;
        var d = s.IndexOf('d');
        if (d < 0) return false;
        var left  = d == 0 ? "1" : s[..d];
        var right = s[(d + 1)..];
        return int.TryParse(left, out count)  && count >= 1 && count <= 100 &&
               int.TryParse(right, out sides) && sides >= 2 && sides <= 10000;
    }

    private static string RgbToHsl(int r, int g, int b)
    {
        var rf = r / 255.0; var gf = g / 255.0; var bf = b / 255.0;
        var max = Math.Max(rf, Math.Max(gf, bf));
        var min = Math.Min(rf, Math.Min(gf, bf));
        var l   = (max + min) / 2;
        if (max == min) return $"hsl(0, 0%, {l:P0})";
        var d = max - min;
        var s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        var h = max == rf ? (gf - bf) / d + (gf < bf ? 6 : 0)
              : max == gf ? (bf - rf) / d + 2
              : (rf - gf) / d + 4;
        h /= 6;
        return $"hsl({h * 360:F0}, {s:P0}, {l:P0})";
    }

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

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