using System.Security.Cryptography;
using System.Text;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;
namespace AxCopilot.Handlers;
///
/// L9-1: 비밀번호 생성기 핸들러. "pwd" 프리픽스로 사용합니다.
///
/// 예: pwd → 기본 16자 비밀번호 5개 생성
/// pwd 24 → 24자 비밀번호 5개 생성
/// pwd 32 strong → 32자 강력 옵션 (대소문자+숫자+특수)
/// pwd 16 alpha → 알파벳+숫자만 (특수문자 제외)
/// pwd 20 pin → 숫자만 (PIN 코드)
/// pwd passphrase → 단어 조합 기억하기 쉬운 패스프레이즈
/// Enter → 클립보드에 복사.
///
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> GetItemsAsync(string query, CancellationToken ct)
{
var q = query.Trim().ToLowerInvariant();
var items = new List();
// 패스프레이즈 모드
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>(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>(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[] 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));
}
}