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

namespace AxCopilot.Handlers;

/// <summary>
/// L20-4: Unix 파일 권한 계산기 핸들러. "perm" 프리픽스로 사용합니다.
///
/// 예: perm                   → 주요 권한 목록
///     perm 755               → rwxr-xr-x 상세 설명
///     perm 644               → rw-r--r-- 상세 설명
///     perm rwxr-xr-x         → 기호 → 숫자 변환
///     perm rw-r--r--         → 기호 → 숫자 변환
///     perm +x 644            → 실행 비트 추가
///     perm -x 755            → 실행 비트 제거
///     perm +w 444            → 쓰기 비트 추가
///     perm umask 022         → umask 적용 결과
///     perm common            → 자주 쓰는 권한 목록
/// Enter → 값 복사.
/// </summary>
public class PermHandler : IActionHandler
{
    public string? Prefix => "perm";

    public PluginMetadata Metadata => new(
        "Perm",
        "Unix 파일 권한 계산기 — chmod 숫자↔기호·umask·권한 설명",
        "1.0",
        "AX");

    private record CommonPerm(string Octal, string Symbol, string Description, string UseCase);

    private static readonly CommonPerm[] Common =
    [
        new("777", "rwxrwxrwx", "모든 사용자 완전 권한",  "임시 스크립트 (보안 주의)"),
        new("755", "rwxr-xr-x", "소유자 완전·그룹/기타 읽기+실행", "실행 파일, 디렉토리"),
        new("750", "rwxr-x---", "소유자 완전·그룹 읽기+실행·기타 없음", "그룹 공유 스크립트"),
        new("700", "rwx------", "소유자만 완전 권한", "개인 스크립트"),
        new("644", "rw-r--r--", "소유자 읽기+쓰기·그룹/기타 읽기만", "일반 파일"),
        new("640", "rw-r-----", "소유자 읽기+쓰기·그룹 읽기·기타 없음", "설정 파일"),
        new("600", "rw-------", "소유자만 읽기+쓰기", "개인 설정·SSH 키"),
        new("444", "r--r--r--", "모든 사용자 읽기만", "공유 읽기 전용"),
        new("400", "r--------", "소유자만 읽기",      "SSL 인증서·개인 키"),
        new("666", "rw-rw-rw-", "모든 사용자 읽기+쓰기 (실행 없음)", "임시 파일"),
        new("664", "rw-rw-r--", "소유자+그룹 읽기+쓰기·기타 읽기", "그룹 협업 파일"),
        new("660", "rw-rw----", "소유자+그룹 읽기+쓰기·기타 없음", "그룹 협업 파일"),
        new("775", "rwxrwxr-x", "소유자+그룹 완전·기타 읽기+실행", "그룹 협업 디렉토리"),
        new("770", "rwxrwx---", "소유자+그룹 완전·기타 없음", "그룹 전용 디렉토리"),
    ];

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

        if (string.IsNullOrWhiteSpace(q))
        {
            items.Add(new LauncherItem("Unix 파일 권한 계산기",
                "perm 755  /  perm rwxr-xr-x  /  perm +x 644  /  perm umask 022  /  perm common",
                null, null, Symbol: "\uE8A5"));
            foreach (var p in Common.Take(6))
                items.Add(new LauncherItem($"{p.Octal}  {p.Symbol}",
                    $"{p.Description}  ({p.UseCase})",
                    null, ("copy", p.Octal), Symbol: "\uE8A5"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        // common
        if (sub is "common" or "list" or "목록")
        {
            items.Add(new LauncherItem("자주 쓰는 파일 권한 목록", "", null, null, Symbol: "\uE8A5"));
            foreach (var p in Common)
                items.Add(new LauncherItem($"{p.Octal}  {p.Symbol}",
                    $"{p.Description}  ·  {p.UseCase}",
                    null, ("copy", p.Octal), Symbol: "\uE8A5"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // umask
        if (sub == "umask" && parts.Length >= 2)
        {
            if (TryParseOctal(parts[1], out var umaskVal))
                items.AddRange(BuildUmask(umaskVal));
            else
                items.Add(ErrorItem("umask 값은 3자리 8진수입니다 (예: 022)"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // +x / -x / +w / -w / +r / -r (비트 수정)
        if (sub.Length == 2 && sub[0] is '+' or '-' && sub[1] is 'r' or 'w' or 'x')
        {
            if (parts.Length >= 2 && TryParseOctal(parts[1], out var baseVal))
                items.AddRange(BuildBitModify(sub[0] == '+', sub[1], baseVal));
            else
                items.Add(ErrorItem($"예: perm {sub} 644"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 기호 표기 (rwxr-xr-x)
        if (sub.Length == 9 && sub.All(c => c is 'r' or 'w' or 'x' or '-'))
        {
            items.AddRange(BuildFromSymbol(sub));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 8진수 표기 (755, 644...)
        if (TryParseOctal(sub, out var octal))
        {
            items.AddRange(BuildFromOctal(octal));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        items.Add(new LauncherItem($"인식할 수 없는 입력: '{sub}'",
            "perm 755  /  perm rwxr-xr-x  /  perm +x 644  /  perm umask 022",
            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("Perm", "클립보드에 복사했습니다.");
            }
            catch { }
        }
        return Task.CompletedTask;
    }

    // ── 빌더 ────────────────────────────────────────────────────────────────

    private static IEnumerable<LauncherItem> BuildFromOctal(int octal)
    {
        var sym  = OctalToSymbol(octal);
        var desc = DescribeOctal(octal);
        var preset = Common.FirstOrDefault(p => p.Octal == octal.ToString("D3"));

        yield return new LauncherItem($"{octal:D3}  →  {sym}",
            preset?.Description ?? desc,
            null, ("copy", octal.ToString("D3")), Symbol: "\uE8A5");

        yield return CopyItem("8진수",       octal.ToString("D3"));
        yield return CopyItem("기호 표기",   sym);
        yield return CopyItem("chmod 명령", $"chmod {octal:D3} <파일>");

        var parts = OctalToParts(octal);
        yield return new LauncherItem("소유자 (Owner)",
            $"{OctetToSymbol(parts[0])}  ({BitsDesc(parts[0])})",
            null, null, Symbol: "\uE8A5");
        yield return new LauncherItem("그룹 (Group)",
            $"{OctetToSymbol(parts[1])}  ({BitsDesc(parts[1])})",
            null, null, Symbol: "\uE8A5");
        yield return new LauncherItem("기타 (Others)",
            $"{OctetToSymbol(parts[2])}  ({BitsDesc(parts[2])})",
            null, null, Symbol: "\uE8A5");

        if (preset != null)
            yield return new LauncherItem("용도", preset.UseCase, null, null, Symbol: "\uE8A5");

        // 관련 권한 제안
        yield return new LauncherItem("── 관련 권한 ──", "", null, null, Symbol: "\uE8A5");
        var related = Common.Where(p => Math.Abs(int.Parse(p.Octal) - octal) <= 11)
                            .Take(4).ToList();
        foreach (var r in related)
            yield return new LauncherItem($"{r.Octal}  {r.Symbol}", r.Description,
                null, ("copy", r.Octal), Symbol: "\uE8A5");
    }

    private static IEnumerable<LauncherItem> BuildFromSymbol(string sym)
    {
        var octal = SymbolToOctal(sym);
        return BuildFromOctal(octal);
    }

    private static IEnumerable<LauncherItem> BuildUmask(int umask)
    {
        var fileDefault = 0666;
        var dirDefault  = 0777;
        var fileResult  = fileDefault & ~umask;
        var dirResult   = dirDefault  & ~umask;

        yield return new LauncherItem($"umask {umask:D3}  →  파일: {fileResult:D3}  디렉토리: {dirResult:D3}",
            "umask 적용 결과", null, null, Symbol: "\uE8A5");
        yield return CopyItem("umask 값",      umask.ToString("D3"));
        yield return CopyItem("기본 파일 권한", fileResult.ToString("D3"));
        yield return CopyItem("기본 디렉토리 권한", dirResult.ToString("D3"));
        yield return CopyItem("파일 기호",       OctalToSymbol(fileResult));
        yield return CopyItem("디렉토리 기호",   OctalToSymbol(dirResult));
        yield return new LauncherItem("설명",
            $"umask {umask:D3}: 파일={OctalToSymbol(fileResult)}, 디렉토리={OctalToSymbol(dirResult)}",
            null, null, Symbol: "\uE8A5");
    }

    private static IEnumerable<LauncherItem> BuildBitModify(bool add, char bit, int baseOctal)
    {
        var mask = bit switch
        {
            'r' => 0444,
            'w' => 0222,
            'x' => 0111,
            _   => 0
        };
        var result = add ? (baseOctal | mask) : (baseOctal & ~mask);
        result &= 0777;

        var label = add ? $"+{bit}" : $"-{bit}";
        yield return new LauncherItem($"{baseOctal:D3} {label}  →  {result:D3}  ({OctalToSymbol(result)})",
            "Enter 복사", null, ("copy", result.ToString("D3")), Symbol: "\uE8A5");
        yield return CopyItem("변경 전", baseOctal.ToString("D3"));
        yield return CopyItem("변경 후", result.ToString("D3"));
        yield return CopyItem("기호",    OctalToSymbol(result));
        yield return CopyItem("chmod",  $"chmod {label} <파일>  (또는 chmod {result:D3} <파일>)");
    }

    // ── 변환 헬퍼 ────────────────────────────────────────────────────────────

    private static bool TryParseOctal(string s, out int val)
    {
        val = 0;
        s   = s.TrimStart('0');
        if (string.IsNullOrEmpty(s)) { val = 0; return true; }
        if (s.Length > 4) return false;
        try { val = Convert.ToInt32(s, 8); return val <= 0777; }
        catch { return false; }
    }

    private static int[] OctalToParts(int octal) =>
        [(octal >> 6) & 7, (octal >> 3) & 7, octal & 7];

    private static string OctetToSymbol(int v) =>
        $"{((v & 4) != 0 ? 'r' : '-')}{((v & 2) != 0 ? 'w' : '-')}{((v & 1) != 0 ? 'x' : '-')}";

    private static string OctalToSymbol(int octal)
    {
        var p = OctalToParts(octal);
        return OctetToSymbol(p[0]) + OctetToSymbol(p[1]) + OctetToSymbol(p[2]);
    }

    private static int SymbolToOctal(string sym)
    {
        int TriBit(int off) =>
            ((sym[off]     == 'r' ? 4 : 0) |
             (sym[off + 1] == 'w' ? 2 : 0) |
             (sym[off + 2] == 'x' ? 1 : 0));
        return (TriBit(0) << 6) | (TriBit(3) << 3) | TriBit(6);
    }

    private static string BitsDesc(int v)
    {
        var parts = new List<string>();
        if ((v & 4) != 0) parts.Add("읽기");
        if ((v & 2) != 0) parts.Add("쓰기");
        if ((v & 1) != 0) parts.Add("실행");
        return parts.Count == 0 ? "없음" : string.Join("·", parts);
    }

    private static string DescribeOctal(int octal)
    {
        var p    = OctalToParts(octal);
        return $"소유자:{BitsDesc(p[0])}  그룹:{BitsDesc(p[1])}  기타:{BitsDesc(p[2])}";
    }

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

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