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

namespace AxCopilot.Handlers;

/// <summary>
/// 텍스트/파일 비교 핸들러. "diff" 프리픽스로 사용합니다.
/// 클립보드 히스토리의 최근 2개 텍스트를 줄 단위로 비교하거나,
/// 파일 2개를 지정하여 비교합니다.
/// 예: diff                → 클립보드 히스토리 최근 2개 비교
///     diff C:\a.txt C:\b.txt → 파일 비교
/// Enter → 비교 결과를 클립보드에 복사.
/// </summary>
public class DiffHandler : IActionHandler
{
    private readonly ClipboardHistoryService? _clipHistory;

    public DiffHandler(ClipboardHistoryService? clipHistory = null)
    {
        _clipHistory = clipHistory;
    }

    public string? Prefix => "diff";

    public PluginMetadata Metadata => new(
        "Diff",
        "텍스트/파일 비교 — diff",
        "1.0",
        "AX");

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

        // 파일 비교 모드
        if (!string.IsNullOrWhiteSpace(q))
        {
            // 파일 2개 지정
            var paths = q.Split(' ', 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
            if (paths.Length == 2 && File.Exists(paths[0]) && File.Exists(paths[1]))
            {
                try
                {
                    var textA = File.ReadAllText(paths[0]);
                    var textB = File.ReadAllText(paths[1]);
                    var result = BuildDiff(textA, textB,
                        Path.GetFileName(paths[0]), Path.GetFileName(paths[1]));

                    return Task.FromResult<IEnumerable<LauncherItem>>(
                    [
                        new LauncherItem(
                            $"파일 비교: {Path.GetFileName(paths[0])} ↔ {Path.GetFileName(paths[1])}",
                            $"{result.Added}줄 추가, {result.Removed}줄 삭제, {result.Same}줄 동일  ·  Enter로 결과 복사",
                            null, result.Text,
                            Symbol: Symbols.File)
                    ]);
                }
                catch (Exception ex)
                {
                    return Task.FromResult<IEnumerable<LauncherItem>>(
                    [
                        new LauncherItem($"파일 읽기 실패: {ex.Message}", "",
                            null, null, Symbol: Symbols.Error)
                    ]);
                }
            }

            // 파일 1개만 있으면 안내
            if (paths.Length >= 1 && (File.Exists(paths[0]) || Directory.Exists(Path.GetDirectoryName(paths[0]) ?? "")))
            {
                return Task.FromResult<IEnumerable<LauncherItem>>(
                [
                    new LauncherItem(
                        "비교할 파일 2개의 경로를 입력하세요",
                        "예: diff C:\\a.txt C:\\b.txt",
                        null, null, Symbol: Symbols.Info)
                ]);
            }
        }

        // 클립보드 히스토리 비교 모드
        var history = _clipHistory?.History;
        if (history == null || history.Count < 2)
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(
            [
                new LauncherItem(
                    "비교할 텍스트가 부족합니다",
                    "클립보드에 2개 이상의 텍스트를 복사하거나, diff [파일A] [파일B]로 파일 비교",
                    null, null, Symbol: Symbols.Info)
            ]);
        }

        var textEntries = history.Where(e => e.IsText).Take(2).ToList();
        if (textEntries.Count < 2)
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(
            [
                new LauncherItem("텍스트 히스토리가 2개 미만입니다", "텍스트를 2번 이상 복사하세요",
                    null, null, Symbol: Symbols.Info)
            ]);
        }

        var older = textEntries[1]; // 이전
        var newer = textEntries[0]; // 최근
        var diff = BuildDiff(older.Text, newer.Text,
            $"이전 ({older.RelativeTime})", $"최근 ({newer.RelativeTime})");

        var items = new List<LauncherItem>
        {
            new(
                $"클립보드 비교: +{diff.Added} -{diff.Removed} ={diff.Same}",
                $"이전 복사 ↔ 최근 복사  ·  Enter로 결과 복사",
                null, diff.Text,
                Symbol: Symbols.History),
        };

        // 미리보기 (변경된 줄 최대 5개)
        var changedLines = diff.Text.Split('\n')
            .Where(l => l.StartsWith("+ ") || l.StartsWith("- "))
            .Take(5);

        foreach (var line in changedLines)
        {
            var symbol = line.StartsWith("+ ") ? "\uE710" : "\uE711"; // + or X
            items.Add(new LauncherItem(
                line,
                "",
                null, null,
                Symbol: symbol));
        }

        // 파일 선택 비교 항목
        items.Add(new LauncherItem(
            "파일 선택하여 비교",
            "파일 선택 대화 상자에서 2개의 파일을 골라 비교합니다",
            null, "__FILE_DIALOG__",
            Symbol: Symbols.File));

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

    public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        // 파일 선택 다이얼로그
        if (item.Data is string s && s == "__FILE_DIALOG__")
        {
            await Task.Delay(200, ct); // 런처 닫힘 대기
            Application.Current?.Dispatcher.Invoke(() =>
            {
                var dlg = new Microsoft.Win32.OpenFileDialog
                {
                    Title = "비교할 첫 번째 파일 선택",
                    Filter = "텍스트 파일|*.txt;*.cs;*.json;*.xml;*.md;*.csv;*.log|모든 파일|*.*"
                };
                if (dlg.ShowDialog() != true) return;
                var fileA = dlg.FileName;

                dlg.Title = "비교할 두 번째 파일 선택";
                if (dlg.ShowDialog() != true) return;
                var fileB = dlg.FileName;

                try
                {
                    var textA = File.ReadAllText(fileA);
                    var textB = File.ReadAllText(fileB);
                    var result = BuildDiff(textA, textB,
                        Path.GetFileName(fileA), Path.GetFileName(fileB));

                    Clipboard.SetText(result.Text);
                    NotificationService.Notify("파일 비교 완료",
                        $"+{result.Added} -{result.Removed} ={result.Same}  ·  결과 클립보드 복사됨");
                }
                catch (Exception ex)
                {
                    NotificationService.Notify("비교 실패", ex.Message);
                }
            });
            return;
        }

        if (item.Data is string text && !string.IsNullOrWhiteSpace(text))
        {
            try { Application.Current?.Dispatcher.Invoke(() => Clipboard.SetText(text)); }
            catch (Exception) { }
            NotificationService.Notify("비교 결과", "클립보드에 복사되었습니다");
        }
    }

    // 간단한 줄 단위 diff
    private static DiffResult BuildDiff(string textA, string textB, string labelA, string labelB)
    {
        var linesA = textA.Split('\n').Select(l => l.TrimEnd('\r')).ToArray();
        var linesB = textB.Split('\n').Select(l => l.TrimEnd('\r')).ToArray();

        var setA = new HashSet<string>(linesA);
        var setB = new HashSet<string>(linesB);

        var sb = new StringBuilder();
        sb.AppendLine($"--- {labelA}");
        sb.AppendLine($"+++ {labelB}");
        sb.AppendLine();

        int added = 0, removed = 0, same = 0;
        int maxLen = Math.Max(linesA.Length, linesB.Length);

        for (int i = 0; i < maxLen; i++)
        {
            var a = i < linesA.Length ? linesA[i] : null;
            var b = i < linesB.Length ? linesB[i] : null;

            if (a == b)
            {
                sb.AppendLine($"  {a}");
                same++;
            }
            else
            {
                if (a != null && !setB.Contains(a))
                {
                    sb.AppendLine($"- {a}");
                    removed++;
                }
                if (b != null && !setA.Contains(b))
                {
                    sb.AppendLine($"+ {b}");
                    added++;
                }
                if (a != null && setB.Contains(a) && a != b)
                {
                    sb.AppendLine($"  {a}");
                    same++;
                }
            }
        }

        return new DiffResult(sb.ToString(), added, removed, same);
    }

    private record DiffResult(string Text, int Added, int Removed, int Same);
}
