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

namespace AxCopilot.Handlers;

/// <summary>
/// 클립보드 파이프라인 핸들러. "pipe" 프리픽스로 사용합니다.
/// 여러 변환을 체이닝하여 클립보드 텍스트를 한 번에 처리합니다.
/// 예: pipe upper > trim > b64e   → 대문자 → 공백제거 → Base64 인코딩
///     pipe sort > unique > number → 정렬 → 중복제거 → 줄번호
///     pipe                        → 사용 가능한 필터 목록
/// Enter → 변환 결과를 클립보드에 복사.
/// </summary>
public class ClipboardPipeHandler : IActionHandler
{
    public string? Prefix => "pipe";

    public PluginMetadata Metadata => new(
        "ClipboardPipe",
        "클립보드 파이프라인 — pipe",
        "1.0",
        "AX");

    private static readonly Dictionary<string, (string Desc, Func<string, string> Fn)> Filters = new(StringComparer.OrdinalIgnoreCase)
    {
        ["upper"]   = ("대문자 변환",       s => s.ToUpperInvariant()),
        ["lower"]   = ("소문자 변환",       s => s.ToLowerInvariant()),
        ["trim"]    = ("앞뒤 공백 제거",    s => s.Trim()),
        ["trimall"] = ("모든 공백 제거",    s => Regex.Replace(s, @"\s+", "", RegexOptions.None, TimeSpan.FromSeconds(1))),
        ["sort"]    = ("줄 정렬 (오름차순)", s => string.Join("\n", s.Split('\n').Order())),
        ["sortd"]   = ("줄 정렬 (내림차순)", s => string.Join("\n", s.Split('\n').OrderDescending())),
        ["unique"]  = ("중복 줄 제거",      s => string.Join("\n", s.Split('\n').Distinct())),
        ["reverse"] = ("줄 순서 뒤집기",    s => string.Join("\n", s.Split('\n').Reverse())),
        ["number"]  = ("줄번호 추가",       s => string.Join("\n", s.Split('\n').Select((l, i) => $"{i + 1}. {l}"))),
        ["quote"]   = ("각 줄 따옴표 감싸기", s => string.Join("\n", s.Split('\n').Select(l => $"\"{l}\""))),
        ["b64e"]    = ("Base64 인코딩",     s => Convert.ToBase64String(Encoding.UTF8.GetBytes(s))),
        ["b64d"]    = ("Base64 디코딩",     s => Encoding.UTF8.GetString(Convert.FromBase64String(s.Trim()))),
        ["urle"]    = ("URL 인코딩",        s => Uri.EscapeDataString(s)),
        ["urld"]    = ("URL 디코딩",        s => Uri.UnescapeDataString(s)),
        ["md"]      = ("마크다운 제거",     s => Regex.Replace(s, @"[#*_`~\[\]()]", "", RegexOptions.None, TimeSpan.FromSeconds(1))),
        ["lines"]   = ("빈 줄 제거",        s => string.Join("\n", s.Split('\n').Where(l => !string.IsNullOrWhiteSpace(l)))),
        ["count"]   = ("글자/단어/줄 수",   s => $"글자: {s.Length}  단어: {s.Split((char[])null!, StringSplitOptions.RemoveEmptyEntries).Length}  줄: {s.Split('\n').Length}"),
        ["csv"]     = ("CSV → 탭 변환",     s => s.Replace(',', '\t')),
        ["tab"]     = ("탭 → CSV 변환",     s => s.Replace('\t', ',')),
    };

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

        if (string.IsNullOrWhiteSpace(q))
        {
            var items = Filters.Select(kv => new LauncherItem(
                kv.Key,
                kv.Value.Desc,
                null, null,
                Symbol: Symbols.Clipboard)).ToList<LauncherItem>();

            items.Insert(0, new LauncherItem(
                "클립보드 파이프라인",
                "필터를 > 로 연결: pipe upper > trim > b64e",
                null, null, Symbol: Symbols.Info));

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

        // 파이프라인 파싱
        var steps = q.Split('>')
            .Select(s => s.Trim())
            .Where(s => !string.IsNullOrEmpty(s))
            .ToArray();

        // 유효성 검사
        var invalid = steps.Where(s => !Filters.ContainsKey(s)).ToArray();
        if (invalid.Length > 0)
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(new[]
            {
                new LauncherItem(
                    $"알 수 없는 필터: {string.Join(", ", invalid)}",
                    $"사용 가능: {string.Join(", ", Filters.Keys.Take(10))} ...",
                    null, null, Symbol: Symbols.Warning)
            }.AsEnumerable());
        }

        // 클립보드 텍스트 읽기
        string? text = null;
        try
        {
            if (Application.Current?.Dispatcher.Invoke(() => Clipboard.ContainsText()) == true)
                text = Application.Current.Dispatcher.Invoke(() => Clipboard.GetText());
        }
        catch (Exception) { }

        if (string.IsNullOrEmpty(text))
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(new[]
            {
                new LauncherItem("클립보드에 텍스트가 없습니다", "텍스트를 복사한 후 시도하세요",
                    null, null, Symbol: Symbols.Warning)
            }.AsEnumerable());
        }

        // 파이프라인 실행
        var result = text;
        var desc = new List<string>();
        try
        {
            foreach (var step in steps)
            {
                result = Filters[step].Fn(result);
                desc.Add(Filters[step].Desc);
            }
        }
        catch (Exception ex)
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(new[]
            {
                new LauncherItem($"파이프라인 실행 오류: {ex.Message}", "입력 데이터를 확인하세요",
                    null, null, Symbol: Symbols.Error)
            }.AsEnumerable());
        }

        var preview = result.Length > 100 ? result[..97] + "…" : result;
        preview = preview.Replace("\r\n", "↵ ").Replace("\n", "↵ ");

        return Task.FromResult<IEnumerable<LauncherItem>>(new[]
        {
            new LauncherItem(
                $"[{string.Join(" → ", steps)}] 결과 적용",
                $"{preview}  ·  Enter로 클립보드 복사",
                null, result,
                Symbol: Symbols.Clipboard)
        }.AsEnumerable());
    }

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is string text)
        {
            try { Application.Current?.Dispatcher.Invoke(() => Clipboard.SetText(text)); }
            catch (Exception) { }
            NotificationService.Notify("파이프라인 완료", "변환 결과가 클립보드에 복사되었습니다");
        }
        return Task.CompletedTask;
    }
}
