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

namespace AxCopilot.Handlers;

/// <summary>
/// L28-4: 클립보드 텍스트 즉시 변환 핸들러. "ap" 프리픽스로 사용합니다.
/// (Advanced Paste — PowerToys Advanced Paste 대응)
///
/// 예: ap                → 클립보드 내용 표시 + 변환 목록
///     ap upper          → 대문자 변환
///     ap lower          → 소문자 변환
///     ap trim           → 앞뒤 공백 제거
///     ap sort           → 줄 정렬
///     ap unique         → 중복 줄 제거
///     ap number         → 줄 번호 추가
///     ap reverse        → 줄 순서 뒤집기
///     ap count          → 글자/단어/줄 수
///     ap json           → JSON 정리 (포맷팅)
///     ap remove blank   → 빈 줄 제거
///     ap replace A B    → A를 B로 전체 치환
/// Enter → 변환된 텍스트를 클립보드에 복사.
/// </summary>
public class ApHandler : IActionHandler
{
    public string? Prefix => "ap";

    public PluginMetadata Metadata => new(
        "텍스트 변환",
        "클립보드 텍스트 즉시 변환 (Advanced Paste)",
        "1.0",
        "AX");

    private static readonly (string Cmd, string Label, string Desc)[] Commands =
    [
        ("upper",   "대문자 변환",        "전체 텍스트를 대문자로"),
        ("lower",   "소문자 변환",        "전체 텍스트를 소문자로"),
        ("trim",    "공백 정리",          "각 줄 앞뒤 공백 제거"),
        ("sort",    "줄 정렬",            "알파벳/가나다 순 줄 정렬"),
        ("rsort",   "줄 역순 정렬",       "역순 줄 정렬"),
        ("unique",  "중복 제거",          "동일 줄 제거 (순서 유지)"),
        ("number",  "줄 번호 추가",       "각 줄 앞에 1. 2. 3. 번호"),
        ("reverse", "줄 순서 뒤집기",     "마지막 줄 → 첫 줄"),
        ("count",   "텍스트 통계",        "글자·단어·줄 수 표시"),
        ("blank",   "빈 줄 제거",         "빈 줄 삭제"),
        ("json",    "JSON 정리",          "JSON 들여쓰기 포맷팅"),
        ("single",  "한 줄로 합치기",     "줄바꿈 → 공백으로 연결"),
        ("slug",    "URL 슬러그",         "소문자 + 하이픈 (공백/특수문자 변환)"),
        ("base64",  "Base64 인코딩",      "텍스트 → Base64"),
        ("decode64","Base64 디코딩",      "Base64 → 텍스트"),
    ];

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

        // 클립보드 텍스트 읽기
        string clipText;
        try
        {
            clipText = Application.Current.Dispatcher.Invoke(() => Clipboard.GetText()) ?? "";
        }
        catch
        {
            clipText = "";
        }

        if (string.IsNullOrEmpty(clipText))
        {
            items.Add(new LauncherItem("클립보드가 비어 있습니다",
                "변환할 텍스트를 먼저 복사하세요",
                null, null, Symbol: Symbols.Clipboard));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var preview = clipText.Length > 80 ? clipText[..77].Replace("\n", " ") + "…" : clipText.Replace("\n", " ");
        int lineCount = clipText.Split('\n').Length;

        // ── replace 명령 ───────────────────────────────────────────────────────
        if (q.StartsWith("replace "))
        {
            var parts = q[8..].Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length >= 2)
            {
                var from = parts[0];
                var to   = parts[1];
                var result = clipText.Replace(from, to, StringComparison.OrdinalIgnoreCase);
                int count = (clipText.Length - result.Length) / Math.Max(from.Length - to.Length, 1);
                items.Add(new LauncherItem(
                    $"치환: '{from}' → '{to}'",
                    $"Enter: 클립보드 갱신",
                    null, ("result", result), Symbol: Symbols.Clipboard));
            }
            else
            {
                items.Add(new LauncherItem("사용법: ap replace {찾을값} {바꿀값}",
                    "예: ap replace hello world", null, null, Symbol: Symbols.Info));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // ── 빈 쿼리 → 전체 명령 목록 ─────────────────────────────────────────
        if (string.IsNullOrWhiteSpace(q))
        {
            items.Add(new LauncherItem(
                $"클립보드: {preview}",
                $"{clipText.Length}자 · {lineCount}줄 · 아래 변환 명령 선택",
                null, null, Symbol: Symbols.Clipboard));

            foreach (var (cmd, label, desc) in Commands)
            {
                items.Add(new LauncherItem(
                    $"ap {cmd}  —  {label}",
                    desc,
                    null, ("cmd", cmd), Symbol: "\uE8AC"));
            }
            items.Add(new LauncherItem(
                "ap replace {A} {B}  —  텍스트 치환",
                "A를 B로 전체 치환",
                null, null, Symbol: "\uE8AC"));

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

        // ── 명령 실행 미리보기 ───────────────────────────────────────────────
        var transformed = Transform(clipText, q);
        if (transformed != null)
        {
            var tPreview = transformed.Length > 120 ? transformed[..117].Replace("\n", " ") + "…" : transformed.Replace("\n", " ");
            var cmdInfo = Commands.FirstOrDefault(c => c.Cmd == q);
            items.Add(new LauncherItem(
                $"{(cmdInfo.Label ?? q)} 결과",
                $"{tPreview}  · Enter: 클립보드 갱신",
                null, ("result", transformed), Symbol: Symbols.Clipboard));
        }
        else
        {
            // 부분 매칭으로 명령 제안
            var matched = Commands.Where(c =>
                c.Cmd.Contains(q) || c.Label.Contains(q, StringComparison.OrdinalIgnoreCase)).ToList();

            if (matched.Count > 0)
            {
                foreach (var (cmd, label, desc) in matched)
                    items.Add(new LauncherItem($"ap {cmd}  —  {label}", desc,
                        null, ("cmd", cmd), Symbol: "\uE8AC"));
            }
            else
            {
                items.Add(new LauncherItem($"'{q}' 알 수 없는 변환 명령",
                    "ap upper/lower/trim/sort/unique/number/reverse/json ...",
                    null, null, Symbol: Symbols.Warning));
            }
        }

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

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is ("result", string result))
        {
            try
            {
                Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(result));
                NotificationService.Notify("ap", "변환 결과가 클립보드에 복사되었습니다.");
            }
            catch (Exception ex)
            {
                NotificationService.Notify("ap", $"복사 실패: {ex.Message}");
            }
        }
        else if (item.Data is ("cmd", string cmd))
        {
            try
            {
                var clipText = Application.Current.Dispatcher.Invoke(() => Clipboard.GetText()) ?? "";
                var result2 = Transform(clipText, cmd);
                if (result2 != null)
                {
                    Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(result2));
                    NotificationService.Notify("ap", "변환 결과가 클립보드에 복사되었습니다.");
                }
            }
            catch (Exception ex)
            {
                NotificationService.Notify("ap", $"변환 실패: {ex.Message}");
            }
        }
        return Task.CompletedTask;
    }

    // ─── 변환 로직 ───────────────────────────────────────────────────────────

    private static string? Transform(string text, string cmd)
    {
        var lines = text.Split('\n').Select(l => l.TrimEnd('\r')).ToArray();

        return cmd switch
        {
            "upper"    => text.ToUpperInvariant(),
            "lower"    => text.ToLowerInvariant(),
            "trim"     => string.Join("\n", lines.Select(l => l.Trim())),
            "sort"     => string.Join("\n", lines.OrderBy(l => l, StringComparer.OrdinalIgnoreCase)),
            "rsort"    => string.Join("\n", lines.OrderByDescending(l => l, StringComparer.OrdinalIgnoreCase)),
            "unique"   => string.Join("\n", lines.Distinct()),
            "number"   => string.Join("\n", lines.Select((l, i) => $"{i + 1}. {l}")),
            "reverse"  => string.Join("\n", lines.Reverse()),
            "blank"    => string.Join("\n", lines.Where(l => !string.IsNullOrWhiteSpace(l))),
            "single"   => string.Join(" ", lines.Where(l => !string.IsNullOrWhiteSpace(l)).Select(l => l.Trim())),
            "count"    => $"글자: {text.Length}  ·  단어: {CountWords(text)}  ·  줄: {lines.Length}  ·  바이트: {Encoding.UTF8.GetByteCount(text)}",
            "json"     => TryFormatJson(text),
            "slug"     => ToSlug(text),
            "base64"   => Convert.ToBase64String(Encoding.UTF8.GetBytes(text)),
            "decode64" => TryDecodeBase64(text),
            _          => null
        };
    }

    private static int CountWords(string text)
        => Regex.Matches(text, @"[\w가-힣]+").Count;

    private static string TryFormatJson(string text)
    {
        try
        {
            var doc = System.Text.Json.JsonDocument.Parse(text);
            using var ms = new System.IO.MemoryStream();
            using var writer = new System.Text.Json.Utf8JsonWriter(ms, new System.Text.Json.JsonWriterOptions { Indented = true });
            doc.WriteTo(writer);
            writer.Flush();
            return Encoding.UTF8.GetString(ms.ToArray());
        }
        catch { return "유효하지 않은 JSON입니다."; }
    }

    private static string ToSlug(string text)
    {
        var slug = text.ToLowerInvariant().Trim();
        slug = Regex.Replace(slug, @"[^a-z0-9가-힣\s-]", "");
        slug = Regex.Replace(slug, @"[\s]+", "-");
        slug = Regex.Replace(slug, @"-{2,}", "-");
        return slug.Trim('-');
    }

    private static string TryDecodeBase64(string text)
    {
        try
        {
            var bytes = Convert.FromBase64String(text.Trim());
            return Encoding.UTF8.GetString(bytes);
        }
        catch { return "유효하지 않은 Base64입니다."; }
    }
}
