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

namespace AxCopilot.Handlers;

/// <summary>
/// L9-1: 비밀번호 생성기 핸들러. "pwd" 프리픽스로 사용합니다.
///
/// 예: pwd                  → 기본 16자 비밀번호 5개 생성
///     pwd 24               → 24자 비밀번호 5개 생성
///     pwd 32 strong        → 32자 강력 옵션 (대소문자+숫자+특수)
///     pwd 16 alpha         → 알파벳+숫자만 (특수문자 제외)
///     pwd 20 pin           → 숫자만 (PIN 코드)
///     pwd passphrase       → 단어 조합 기억하기 쉬운 패스프레이즈
/// Enter → 클립보드에 복사.
/// </summary>
public class PasswordGenHandler : IActionHandler
{
    public string? Prefix => "pwd";

    public PluginMetadata Metadata => new(
        "PasswordGen",
        "비밀번호 생성기 — 길이 · 복잡도 · 패스프레이즈",
        "1.0",
        "AX");

    private const string LowerChars   = "abcdefghijklmnopqrstuvwxyz";
    private const string UpperChars   = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private const string DigitChars   = "0123456789";
    private const string SpecialChars = "!@#$%^&*()-_=+[]{}|;:,.<>?";

    // 기억하기 쉬운 단어 목록 (간결한 영단어)
    private static readonly string[] WordList =
    [
        "apple","bridge","cloud","dawn","eagle","flame","grape","harbor",
        "ivory","jungle","kite","lemon","maple","night","ocean","pearl",
        "quartz","river","storm","tiger","ultra","violet","water","xenon",
        "yellow","zenith","amber","blaze","cedar","delta","ember","frost",
        "glass","honey","iron","jade","knot","lunar","mango","nova",
        "orbit","prism","quest","range","solar","track","umbra","valor",
    ];

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

        // 패스프레이즈 모드
        if (q.StartsWith("passphrase") || q.StartsWith("phrase"))
        {
            var wordCount = 4;
            var parts2    = q.Split(' ');
            if (parts2.Length >= 2 && int.TryParse(parts2[1], out var wc))
                wordCount = Math.Clamp(wc, 2, 8);

            items.Add(new LauncherItem(
                "패스프레이즈 생성",
                $"{wordCount}단어 조합 · 기억하기 쉬운 형식",
                null, null, Symbol: "\uE8D4"));

            for (int i = 0; i < 5; i++)
            {
                var phrase = GeneratePassphrase(wordCount);
                var strength = EstimateEntropy(phrase);
                items.Add(new LauncherItem(
                    phrase,
                    $"엔트로피: ~{strength}bit",
                    null,
                    ("copy", phrase),
                    Symbol: "\uE8D4"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 파라미터 파싱: [길이] [모드]
        int    length  = 16;
        string mode    = "strong";

        var parts = q.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length >= 1 && int.TryParse(parts[0], out var l))
        {
            length = Math.Clamp(l, 4, 128);
            if (parts.Length >= 2) mode = parts[1];
        }
        else if (parts.Length >= 1 && parts[0] is "strong" or "alpha" or "pin" or "simple")
        {
            mode = parts[0];
        }

        // 문자 집합 결정
        var charset = BuildCharset(mode);

        // 강도 표시
        var modeLabel = mode switch
        {
            "alpha"  => "알파뉴메릭 (대소문자+숫자)",
            "pin"    => "숫자 PIN",
            "simple" => "간단 (소문자+숫자)",
            _        => "강력 (대소문자+숫자+특수)",
        };

        items.Add(new LauncherItem(
            $"비밀번호 생성  {length}자",
            modeLabel,
            null, null, Symbol: "\uE8D4"));

        // 5개 후보 생성
        for (int i = 0; i < 5; i++)
        {
            var pw       = GeneratePassword(charset, length, mode);
            var strength = GetStrengthLabel(pw);
            items.Add(new LauncherItem(
                pw,
                strength,
                null,
                ("copy", pw),
                Symbol: "\uE8D4"));
        }

        // 옵션 안내
        items.Add(new LauncherItem(
            "pwd <길이> alpha",
            "알파뉴메릭 (특수문자 제외)",
            null, null, Symbol: "\uE946"));
        items.Add(new LauncherItem(
            "pwd <길이> pin",
            "숫자만 (PIN 코드)",
            null, null, Symbol: "\uE946"));
        items.Add(new LauncherItem(
            "pwd passphrase",
            "단어 조합 패스프레이즈",
            null, null, Symbol: "\uE946"));

        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is ("copy", string pw))
        {
            try
            {
                System.Windows.Application.Current.Dispatcher.Invoke(
                    () => Clipboard.SetText(pw));
                NotificationService.Notify("비밀번호", "클립보드에 복사했습니다.");
            }
            catch { /* 비핵심 */ }
        }
        return Task.CompletedTask;
    }

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

    private static string BuildCharset(string mode) => mode switch
    {
        "pin"    => DigitChars,
        "alpha"  => LowerChars + UpperChars + DigitChars,
        "simple" => LowerChars + DigitChars,
        _        => LowerChars + UpperChars + DigitChars + SpecialChars,
    };

    private static string GeneratePassword(string charset, int length, string mode)
    {
        // strong 모드: 각 카테고리 최소 1개 보장
        if (mode == "strong" && length >= 4)
        {
            var mandatory = new[]
            {
                RandomChar(LowerChars),
                RandomChar(UpperChars),
                RandomChar(DigitChars),
                RandomChar(SpecialChars),
            };

            var remaining = length - mandatory.Length;
            var bulk      = Enumerable.Range(0, remaining)
                                      .Select(_ => RandomChar(charset))
                                      .ToList();

            var all = mandatory.Concat(bulk).ToArray();
            Shuffle(all);
            return new string(all);
        }

        // alpha/pin/simple
        return new string(Enumerable.Range(0, length)
                                    .Select(_ => RandomChar(charset))
                                    .ToArray());
    }

    private static string GeneratePassphrase(int wordCount)
    {
        var words = Enumerable.Range(0, wordCount)
                              .Select(_ => WordList[RandomInt(WordList.Length)]);
        var num   = RandomInt(9000) + 1000;
        var sep   = new[] { "-", "_", ".", "!" }[RandomInt(4)];
        return string.Join(sep, words) + sep + num;
    }

    private static char RandomChar(string charset) =>
        charset[RandomInt(charset.Length)];

    private static int RandomInt(int max) =>
        (int)(RandomNumberGenerator.GetInt32(int.MaxValue) % max);

    private static void Shuffle<T>(T[] arr)
    {
        for (int i = arr.Length - 1; i > 0; i--)
        {
            var j = RandomInt(i + 1);
            (arr[i], arr[j]) = (arr[j], arr[i]);
        }
    }

    private static string GetStrengthLabel(string pw)
    {
        var hasLower   = pw.Any(char.IsLower);
        var hasUpper   = pw.Any(char.IsUpper);
        var hasDigit   = pw.Any(char.IsDigit);
        var hasSpecial = pw.Any(c => SpecialChars.Contains(c));
        var types      = new[] { hasLower, hasUpper, hasDigit, hasSpecial }.Count(b => b);

        var strength = (pw.Length, types) switch
        {
            ( >= 24, >= 4) => "매우 강함 🔐",
            ( >= 16, >= 3) => "강함 🔒",
            ( >= 12, >= 2) => "보통 🔑",
            _              => "약함 ⚠",
        };
        return $"{strength}  ·  {pw.Length}자";
    }

    private static int EstimateEntropy(string pw)
    {
        // 간략 엔트로피 추정: log2(charset^length)
        var hasLower   = pw.Any(char.IsLower);
        var hasUpper   = pw.Any(char.IsUpper);
        var hasDigit   = pw.Any(char.IsDigit);
        var hasSpecial = pw.Any(c => !char.IsLetterOrDigit(c));
        var pool       = (hasLower ? 26 : 0) + (hasUpper ? 26 : 0)
                       + (hasDigit ? 10 : 0) + (hasSpecial ? 32 : 0);
        if (pool == 0) pool = 36;
        return (int)(pw.Length * Math.Log2(pool));
    }
}
