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

namespace AxCopilot.Handlers;

/// <summary>
/// L15-4: Markdown 분석기 핸들러. "md" 프리픽스로 사용합니다.
///
/// 예: md                     → 클립보드 Markdown 분석 (구조·통계)
///     md toc                 → 목차(TOC) 생성
///     md strip               → Markdown 기호 제거 → 순수 텍스트
///     md count               → 단어·줄·코드블록 수 세기
///     md links               → 링크 목록 추출
///     md images              → 이미지 목록 추출
/// Enter → 결과를 클립보드에 복사.
/// </summary>
public partial class MdHandler : IActionHandler
{
    public string? Prefix => "md";

    public PluginMetadata Metadata => new(
        "MD",
        "Markdown 분석기 — 구조 분석 · TOC · 기호 제거 · 링크 추출",
        "1.0",
        "AX");

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

        // 클립보드에서 텍스트 읽기
        string? clipboard = null;
        try
        {
            System.Windows.Application.Current.Dispatcher.Invoke(() =>
            {
                if (Clipboard.ContainsText())
                    clipboard = Clipboard.GetText();
            });
        }
        catch { /* 클립보드 접근 실패 */ }

        if (string.IsNullOrWhiteSpace(q))
        {
            items.Add(new LauncherItem("Markdown 분석기",
                "클립보드 Markdown 분석  ·  md toc / strip / count / links / images",
                null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("md toc",    "목차(TOC) 생성",         null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("md strip",  "Markdown 기호 제거",     null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("md count",  "단어·줄·코드블록 통계", null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("md links",  "링크 목록 추출",          null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("md images", "이미지 목록 추출",        null, null, Symbol: "\uE8A5"));

            if (string.IsNullOrWhiteSpace(clipboard))
            {
                items.Add(new LauncherItem("클립보드가 비어 있습니다",
                    "Markdown 텍스트를 복사한 뒤 사용하세요", null, null, Symbol: "\uE946"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }

            // 간단 미리보기 통계
            var stat = QuickStat(clipboard);
            items.Add(new LauncherItem("── 클립보드 미리보기 ──", "", null, null, Symbol: "\uE8A5"));
            items.Add(new LauncherItem("클립보드 Markdown 분석", stat, null, ("copy", stat), Symbol: "\uE8A5"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 클립보드 없으면 서브커맨드도 안내만
        if (string.IsNullOrWhiteSpace(clipboard))
        {
            items.Add(new LauncherItem("클립보드가 비어 있습니다",
                "Markdown 텍스트를 복사한 뒤 사용하세요", null, null, Symbol: "\uE946"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var sub = q.Split(' ')[0].ToLowerInvariant();
        switch (sub)
        {
            case "toc":
                items.AddRange(BuildTocItems(clipboard));
                break;

            case "strip":
            case "plain":
            case "text":
                items.AddRange(BuildStripItems(clipboard));
                break;

            case "count":
            case "stat":
            case "stats":
                items.AddRange(BuildCountItems(clipboard));
                break;

            case "links":
            case "link":
                items.AddRange(BuildLinkItems(clipboard));
                break;

            case "images":
            case "image":
            case "img":
                items.AddRange(BuildImageItems(clipboard));
                break;

            default:
                items.Add(new LauncherItem("알 수 없는 서브커맨드",
                    "toc · strip · count · links · images", null, null, Symbol: "\uE783"));
                break;
        }

        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("MD", "클립보드에 복사했습니다.");
            }
            catch { /* 비핵심 */ }
        }
        return Task.CompletedTask;
    }

    // ── 분석 빌더 ─────────────────────────────────────────────────────────────

    private static string QuickStat(string md)
    {
        var lines      = md.Split('\n');
        var headings   = lines.Count(l => HeadingRegex().IsMatch(l));
        var codeBlocks = CountCodeBlocks(md);
        var links      = LinkRegex().Matches(md).Count;
        var words      = WordCount(md);
        return $"{lines.Length}줄  ·  제목 {headings}개  ·  코드블록 {codeBlocks}개  ·  링크 {links}개  ·  단어 {words}개";
    }

    private static List<LauncherItem> BuildTocItems(string md)
    {
        var items    = new List<LauncherItem>();
        var lines    = md.Split('\n');
        var headings = new List<(int Level, string Text, string Anchor)>();

        foreach (var line in lines)
        {
            var m = HeadingRegex().Match(line);
            if (!m.Success) continue;
            var level  = m.Groups[1].Value.Length;
            var text   = m.Groups[2].Value.Trim();
            var anchor = MakeAnchor(text);
            headings.Add((level, text, anchor));
        }

        if (headings.Count == 0)
        {
            items.Add(new LauncherItem("제목(#)이 없습니다", "Markdown 제목이 없으면 TOC를 생성할 수 없습니다",
                null, null, Symbol: "\uE946"));
            return items;
        }

        var sb = new StringBuilder();
        foreach (var (level, text, anchor) in headings)
        {
            var indent = new string(' ', (level - 1) * 2);
            sb.AppendLine($"{indent}- [{text}](#{anchor})");
        }
        var toc = sb.ToString().TrimEnd();

        items.Add(new LauncherItem($"TOC 생성 완료  ({headings.Count}개 제목)",
            "Enter → 전체 TOC 복사", null, ("copy", toc), Symbol: "\uE8A5"));

        foreach (var (level, text, anchor) in headings.Take(20))
        {
            var prefix = new string('#', level) + " ";
            var entry  = $"- [{text}](#{anchor})";
            items.Add(new LauncherItem($"{prefix}{text}", entry, null, ("copy", entry), Symbol: "\uE8A5"));
        }
        if (headings.Count > 20)
            items.Add(new LauncherItem($"… 외 {headings.Count - 20}개", "전체 복사는 첫 항목 Enter", null, null, Symbol: "\uE8A5"));

        return items;
    }

    private static List<LauncherItem> BuildStripItems(string md)
    {
        var items = new List<LauncherItem>();
        var plain = StripMarkdown(md);
        var preview = plain.Length > 80 ? plain[..80] + "…" : plain;

        items.Add(new LauncherItem("Markdown 기호 제거 완료",
            $"Enter → 순수 텍스트 복사  ({plain.Length}자)", null, ("copy", plain), Symbol: "\uE8A5"));
        items.Add(new LauncherItem("미리보기", preview, null, ("copy", plain), Symbol: "\uE8A5"));
        return items;
    }

    private static List<LauncherItem> BuildCountItems(string md)
    {
        var items = new List<LauncherItem>();
        var lines = md.Split('\n');

        var totalLines    = lines.Length;
        var blankLines    = lines.Count(l => string.IsNullOrWhiteSpace(l));
        var codeBlockCount= CountCodeBlocks(md);
        var headingCount  = lines.Count(l => HeadingRegex().IsMatch(l));
        var listCount     = lines.Count(l => ListRegex().IsMatch(l));
        var linkCount     = LinkRegex().Matches(md).Count;
        var imageCount    = ImageRegex().Matches(md).Count;
        var boldCount     = BoldRegex().Matches(md).Count;
        var words         = WordCount(md);
        var chars         = md.Length;
        var charsNoSpace  = md.Replace(" ", "").Replace("\n", "").Replace("\r", "").Length;

        items.Add(new LauncherItem($"Markdown 통계", $"{totalLines}줄  ·  {words}단어  ·  {chars}자",
            null, ("copy", $"줄 {totalLines} · 단어 {words} · 문자 {chars}"), Symbol: "\uE8A5"));

        items.Add(new LauncherItem("전체 줄 수",      $"{totalLines}줄  (공백 {blankLines}줄)",         null, ("copy", $"{totalLines}"), Symbol: "\uE8A5"));
        items.Add(new LauncherItem("단어 수",          $"{words}단어",                                  null, ("copy", $"{words}"),      Symbol: "\uE8A5"));
        items.Add(new LauncherItem("문자 수",          $"{chars}자  (공백 제외 {charsNoSpace}자)",       null, ("copy", $"{chars}"),      Symbol: "\uE8A5"));
        items.Add(new LauncherItem("제목(#) 수",       $"{headingCount}개",                             null, ("copy", $"{headingCount}"), Symbol: "\uE8A5"));
        items.Add(new LauncherItem("코드 블록 수",     $"{codeBlockCount}개",                           null, ("copy", $"{codeBlockCount}"), Symbol: "\uE8A5"));
        items.Add(new LauncherItem("목록 항목 수",     $"{listCount}개",                                null, ("copy", $"{listCount}"), Symbol: "\uE8A5"));
        items.Add(new LauncherItem("링크 수",          $"{linkCount}개",                                null, ("copy", $"{linkCount}"), Symbol: "\uE8A5"));
        items.Add(new LauncherItem("이미지 수",        $"{imageCount}개",                               null, ("copy", $"{imageCount}"), Symbol: "\uE8A5"));
        items.Add(new LauncherItem("강조(**bold**) 수", $"{boldCount}개",                               null, ("copy", $"{boldCount}"), Symbol: "\uE8A5"));

        return items;
    }

    private static List<LauncherItem> BuildLinkItems(string md)
    {
        var items   = new List<LauncherItem>();
        var matches = LinkRegex().Matches(md);

        if (matches.Count == 0)
        {
            items.Add(new LauncherItem("링크 없음", "클립보드 Markdown에 링크([text](url))가 없습니다",
                null, null, Symbol: "\uE946"));
            return items;
        }

        var allUrls = string.Join("\n", matches.Cast<Match>().Select(m => m.Groups[2].Value));
        items.Add(new LauncherItem($"링크 {matches.Count}개 발견",
            "Enter → 전체 URL 목록 복사", null, ("copy", allUrls), Symbol: "\uE8A5"));

        foreach (Match m in matches.Cast<Match>().Take(25))
        {
            var text = m.Groups[1].Value;
            var url  = m.Groups[2].Value;
            var display = text.Length > 30 ? text[..30] + "…" : text;
            items.Add(new LauncherItem(display, url, null, ("copy", url), Symbol: "\uE8A5"));
        }
        if (matches.Count > 25)
            items.Add(new LauncherItem($"… 외 {matches.Count - 25}개", "전체 복사는 첫 항목 Enter", null, null, Symbol: "\uE8A5"));

        return items;
    }

    private static List<LauncherItem> BuildImageItems(string md)
    {
        var items   = new List<LauncherItem>();
        var matches = ImageRegex().Matches(md);

        if (matches.Count == 0)
        {
            items.Add(new LauncherItem("이미지 없음", "클립보드 Markdown에 이미지(![alt](url))가 없습니다",
                null, null, Symbol: "\uE946"));
            return items;
        }

        var allUrls = string.Join("\n", matches.Cast<Match>().Select(m => m.Groups[2].Value));
        items.Add(new LauncherItem($"이미지 {matches.Count}개 발견",
            "Enter → 전체 URL 목록 복사", null, ("copy", allUrls), Symbol: "\uE8A5"));

        foreach (Match m in matches.Cast<Match>().Take(25))
        {
            var alt = m.Groups[1].Value;
            var url = m.Groups[2].Value;
            var display = string.IsNullOrWhiteSpace(alt) ? "(alt 없음)" : (alt.Length > 30 ? alt[..30] + "…" : alt);
            items.Add(new LauncherItem(display, url, null, ("copy", url), Symbol: "\uE8A5"));
        }

        return items;
    }

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

    private static string StripMarkdown(string md)
    {
        var s = md;
        // 코드 블록 제거
        s = Regex.Replace(s, @"```[\s\S]*?```", "", RegexOptions.Multiline);
        s = Regex.Replace(s, @"`[^`]+`", "");
        // 제목 기호 제거
        s = Regex.Replace(s, @"^#{1,6}\s+", "", RegexOptions.Multiline);
        // 이미지, 링크 → 텍스트만
        s = Regex.Replace(s, @"!\[([^\]]*)\]\([^\)]*\)", "$1");
        s = Regex.Replace(s, @"\[([^\]]+)\]\([^\)]+\)", "$1");
        // 강조 기호 제거
        s = Regex.Replace(s, @"\*{1,3}([^*]+)\*{1,3}", "$1");
        s = Regex.Replace(s, @"_{1,3}([^_]+)_{1,3}", "$1");
        // 인용 기호
        s = Regex.Replace(s, @"^>\s+", "", RegexOptions.Multiline);
        // 목록 기호
        s = Regex.Replace(s, @"^[\-\*\+]\s+", "", RegexOptions.Multiline);
        s = Regex.Replace(s, @"^\d+\.\s+", "", RegexOptions.Multiline);
        // 수평선
        s = Regex.Replace(s, @"^[-*_]{3,}\s*$", "", RegexOptions.Multiline);
        // 다중 공백 정리
        s = Regex.Replace(s, @"\n{3,}", "\n\n");
        return s.Trim();
    }

    private static string MakeAnchor(string text)
    {
        var s = text.ToLowerInvariant();
        s = Regex.Replace(s, @"[^\w\s\-가-힣]", "");
        s = Regex.Replace(s, @"\s+", "-");
        return s;
    }

    private static int CountCodeBlocks(string md)
    {
        var matches = Regex.Matches(md, @"^```", RegexOptions.Multiline);
        return matches.Count / 2;
    }

    private static int WordCount(string md)
    {
        var plain = StripMarkdown(md);
        return Regex.Matches(plain, @"\S+").Count;
    }

    [GeneratedRegex(@"^(#{1,6})\s+(.+)$", RegexOptions.Multiline)]
    private static partial Regex HeadingRegex();

    [GeneratedRegex(@"^\s*[\-\*\+]\s+|^\s*\d+\.\s+", RegexOptions.Multiline)]
    private static partial Regex ListRegex();

    [GeneratedRegex(@"\[([^\]]+)\]\(([^\)]+)\)")]
    private static partial Regex LinkRegex();

    [GeneratedRegex(@"!\[([^\]]*)\]\(([^\)]+)\)")]
    private static partial Regex ImageRegex();

    [GeneratedRegex(@"\*{2,3}[^*]+\*{2,3}")]
    private static partial Regex BoldRegex();
}
