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

namespace AxCopilot.Handlers;

/// <summary>
/// L8-2: 아카이브 관리 핸들러. "zip" 프리픽스로 사용합니다.
///
/// 예: zip                        → 사용법 안내
///     zip C:\archive.zip         → zip 내 파일 목록 미리보기
///     zip list C:\archive.zip    → 파일 목록 (클립보드 복사)
///     zip extract C:\archive.zip → 같은 폴더에 압축 해제
///     zip extract C:\a.zip C:\target → 지정 폴더에 압축 해제
///     zip folder C:\MyFolder     → 폴더를 zip으로 압축
/// 경로 미입력 시 클립보드 경로 자동 감지.
/// </summary>
public class ZipHandler : IActionHandler
{
    public string? Prefix => "zip";

    public PluginMetadata Metadata => new(
        "Zip",
        "아카이브 관리 — zip 목록 · 압축 해제 · 폴더 압축",
        "1.0",
        "AX");

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

        if (string.IsNullOrWhiteSpace(q))
        {
            var clipPath = GetClipboardPath();
            if (!string.IsNullOrEmpty(clipPath))
            {
                var isZip    = clipPath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase);
                var isFolder = Directory.Exists(clipPath);

                if (isZip && File.Exists(clipPath))
                {
                    var info = GetZipInfo(clipPath);
                    items.Add(new LauncherItem(
                        Path.GetFileName(clipPath),
                        $"{info.Count}개 파일  ·  {info.TotalSizeMb:F1} MB (압축 전)",
                        null, null, Symbol: "\uE8B7"));

                    items.Add(new LauncherItem(
                        "zip list",
                        "파일 목록 표시 및 클립보드 복사",
                        null, ("list", clipPath), Symbol: "\uE8A4"));

                    items.Add(new LauncherItem(
                        "zip extract",
                        $"압축 해제 → {Path.GetDirectoryName(clipPath)}",
                        null, ("extract", clipPath, ""), Symbol: "\uE8B7"));
                }
                else if (isFolder)
                {
                    var outputPath = clipPath.TrimEnd('\\', '/') + ".zip";
                    items.Add(new LauncherItem(
                        "zip folder",
                        $"{Path.GetFileName(clipPath)} → {Path.GetFileName(outputPath)}",
                        null, ("compress", clipPath, outputPath), Symbol: "\uE8B7"));
                }
            }

            items.Add(new LauncherItem(
                "zip <경로>",
                "zip 파일 목록 미리보기",
                null, null, Symbol: "\uE8B7"));
            items.Add(new LauncherItem(
                "zip extract <경로>",
                "압축 해제",
                null, null, Symbol: "\uE8B7"));
            items.Add(new LauncherItem(
                "zip folder <폴더>",
                "폴더를 zip으로 압축",
                null, null, Symbol: "\uE8B7"));

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

        // 서브커맨드 파싱
        var parts = q.Split(' ', 3);
        var sub   = parts[0].ToLowerInvariant();

        // "extract" 서브커맨드
        if (sub == "extract" || sub == "unzip")
        {
            var zipPath   = parts.Length >= 2 ? parts[1].Trim('"') : "";
            var targetDir = parts.Length >= 3 ? parts[2].Trim('"') : "";

            if (string.IsNullOrEmpty(zipPath))
            {
                var clip = GetClipboardPath();
                if (!string.IsNullOrEmpty(clip) && File.Exists(clip)) zipPath = clip;
            }

            if (!File.Exists(zipPath))
            {
                items.Add(new LauncherItem("파일을 찾을 수 없음", zipPath, null, null, Symbol: "\uE783"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }

            var dest = string.IsNullOrEmpty(targetDir)
                ? Path.Combine(Path.GetDirectoryName(zipPath)!,
                               Path.GetFileNameWithoutExtension(zipPath))
                : targetDir;

            var info = GetZipInfo(zipPath);
            items.Add(new LauncherItem(
                $"압축 해제 — {info.Count}개 파일",
                $"→ {dest}",
                null,
                ("extract", zipPath, dest),
                Symbol: "\uE8B7"));

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

        // "folder" 또는 "compress" 서브커맨드
        if (sub == "folder" || sub == "compress")
        {
            var srcFolder = parts.Length >= 2 ? parts[1].Trim('"') : "";
            if (!Directory.Exists(srcFolder))
            {
                var clip = GetClipboardPath();
                if (!string.IsNullOrEmpty(clip) && Directory.Exists(clip)) srcFolder = clip;
                else
                {
                    items.Add(new LauncherItem("폴더를 찾을 수 없음", srcFolder, null, null, Symbol: "\uE783"));
                    return Task.FromResult<IEnumerable<LauncherItem>>(items);
                }
            }

            var outputZip = parts.Length >= 3
                ? parts[2].Trim('"')
                : srcFolder.TrimEnd('\\', '/') + ".zip";

            var fileCount = Directory.GetFiles(srcFolder, "*", SearchOption.AllDirectories).Length;
            items.Add(new LauncherItem(
                $"압축 — {fileCount}개 파일",
                $"{Path.GetFileName(srcFolder)} → {Path.GetFileName(outputZip)}",
                null,
                ("compress", srcFolder, outputZip),
                Symbol: "\uE8B7"));

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

        // "list" 서브커맨드 또는 직접 경로 입력
        var zipFilePath = (sub == "list" && parts.Length >= 2)
            ? parts[1].Trim('"')
            : q.Trim('"');

        if (!File.Exists(zipFilePath))
        {
            var clip = GetClipboardPath();
            zipFilePath = (!string.IsNullOrEmpty(clip) && File.Exists(clip)) ? clip : zipFilePath;
        }

        if (!File.Exists(zipFilePath))
        {
            items.Add(new LauncherItem("파일을 찾을 수 없음", zipFilePath, null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 목록 미리보기
        try
        {
            using var archive = ZipFile.OpenRead(zipFilePath);
            var entries = archive.Entries.OrderBy(e => e.FullName).ToList();
            var totalSize = entries.Sum(e => e.Length);

            items.Add(new LauncherItem(
                Path.GetFileName(zipFilePath),
                $"{entries.Count}개 항목  ·  {totalSize / 1024.0 / 1024.0:F1} MB",
                null,
                ("list", zipFilePath),
                Symbol: "\uE8B7"));

            items.Add(new LauncherItem(
                "압축 해제 →",
                Path.Combine(Path.GetDirectoryName(zipFilePath)!,
                             Path.GetFileNameWithoutExtension(zipFilePath)),
                null,
                ("extract", zipFilePath, ""),
                Symbol: "\uE8B7"));

            foreach (var entry in entries.Take(20))
            {
                items.Add(new LauncherItem(
                    entry.FullName,
                    $"{entry.Length / 1024.0:F0} KB",
                    null,
                    ("copy_entry", entry.FullName),
                    Symbol: entry.FullName.EndsWith('/') ? "\uED25" : "\uE8A5"));
            }

            if (entries.Count > 20)
                items.Add(new LauncherItem(
                    $"… +{entries.Count - 20}개 더",
                    "전체 목록: 첫 항목 Enter → 클립보드 복사",
                    null, null, Symbol: "\uE712"));
        }
        catch (Exception ex)
        {
            items.Add(new LauncherItem("zip 읽기 실패", ex.Message, null, null, Symbol: "\uE783"));
        }

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

    public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        switch (item.Data)
        {
            case ("list", string zipPath):
                try
                {
                    using var archive = ZipFile.OpenRead(zipPath);
                    var list = string.Join("\n", archive.Entries.Select(e => e.FullName));
                    TryCopyToClipboard(list);
                    NotificationService.Notify("Zip", $"{archive.Entries.Count}개 항목을 클립보드에 복사했습니다.");
                }
                catch (Exception ex)
                {
                    NotificationService.Notify("Zip 오류", ex.Message);
                }
                break;

            case ("extract", string zipPath, string targetDir):
                await Task.Run(() =>
                {
                    var dest = string.IsNullOrEmpty(targetDir)
                        ? Path.Combine(Path.GetDirectoryName(zipPath)!,
                                       Path.GetFileNameWithoutExtension(zipPath))
                        : targetDir;

                    ZipFile.ExtractToDirectory(zipPath, dest, overwriteFiles: true);
                    NotificationService.Notify("압축 해제 완료", dest);
                }, ct);
                break;

            case ("compress", string srcFolder, string outputZip):
                await Task.Run(() =>
                {
                    if (File.Exists(outputZip)) File.Delete(outputZip);
                    ZipFile.CreateFromDirectory(srcFolder, outputZip);
                    var sizeMb = new FileInfo(outputZip).Length / 1024.0 / 1024.0;
                    NotificationService.Notify(
                        "압축 완료",
                        $"{Path.GetFileName(outputZip)}  ({sizeMb:F1} MB)");
                }, ct);
                break;

            case ("copy_entry", string entryName):
                TryCopyToClipboard(entryName);
                break;
        }
    }

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

    private static (int Count, double TotalSizeMb) GetZipInfo(string zipPath)
    {
        try
        {
            using var archive = ZipFile.OpenRead(zipPath);
            return (archive.Entries.Count, archive.Entries.Sum(e => e.Length) / 1024.0 / 1024.0);
        }
        catch { return (0, 0); }
    }

    private static string? GetClipboardPath()
    {
        try
        {
            string? text = null;
            System.Windows.Application.Current.Dispatcher.Invoke(() =>
            {
                if (Clipboard.ContainsText())
                    text = Clipboard.GetText()?.Trim().Trim('"');
            });
            return text;
        }
        catch { return null; }
    }

    private static void TryCopyToClipboard(string text)
    {
        try
        {
            System.Windows.Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(text));
        }
        catch { /* 비핵심 */ }
    }
}
