using Microsoft.Win32;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// 앱 제거 핸들러. "uninstall" 프리픽스로 사용합니다.
/// 예: uninstall        → 설치된 앱 전체 목록
///     uninstall chrome → "chrome" 포함 앱 필터
/// Enter 시 해당 앱의 제거 프로그램 실행.
/// </summary>
public class UninstallHandler : IActionHandler
{
    public string? Prefix => "uninstall";

    public PluginMetadata Metadata => new(
        "Uninstall",
        "앱 제거 — uninstall 뒤에 앱 이름 입력",
        "1.0",
        "AX");

    // 레지스트리 언인스톨 키 경로
    private static readonly string[] _regPaths =
    {
        @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
        @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall",
    };

    // 캐시: 30초
    private static (DateTime At, List<InstalledApp> Apps)? _cache;
    private static readonly TimeSpan CacheTtl = TimeSpan.FromSeconds(30);

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

        if (string.IsNullOrWhiteSpace(q))
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(
            [
                new LauncherItem(
                    "제거할 앱 이름을 입력하세요",
                    "예: uninstall chrome  ·  uninstall office",
                    null, null, Symbol: Symbols.Uninstall)
            ]);
        }

        var apps = GetInstalledApps();
        var filtered = apps
            .Where(a => a.Name.Contains(q, StringComparison.OrdinalIgnoreCase) ||
                        (a.Publisher?.Contains(q, StringComparison.OrdinalIgnoreCase) ?? false))
            .OrderBy(a => a.Name)
            .Take(12)
            .ToList();

        if (!filtered.Any())
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(
            [
                new LauncherItem(
                    "검색 결과 없음",
                    $"'{q}'에 해당하는 설치된 앱이 없습니다",
                    null, null, Symbol: Symbols.Info)
            ]);
        }

        var items = filtered.Select(a => new LauncherItem(
            a.Name,
            BuildSubtitle(a),
            null,
            a,
            Symbol: Symbols.Uninstall)).ToList();

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

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is not InstalledApp app) return Task.CompletedTask;

        if (string.IsNullOrWhiteSpace(app.UninstallString))
        {
            LogService.Warn($"제거 문자열 없음: {app.Name}");
            return Task.CompletedTask;
        }

        try
        {
            // UninstallString이 "msiexec /x {GUID}" 또는 경로+args 형태
            var uninstall = app.UninstallString.Trim();

            if (uninstall.StartsWith("msiexec", StringComparison.OrdinalIgnoreCase))
            {
                // msiexec 직접 실행
                var args = uninstall.Substring("msiexec".Length).Trim();
                System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(
                    "msiexec.exe", args) { UseShellExecute = true });
            }
            else
            {
                // 따옴표로 둘러싸인 경로 + 나머지 인수 분리
                string exe, arguments;
                if (uninstall.StartsWith('"'))
                {
                    var close = uninstall.IndexOf('"', 1);
                    exe = close > 0 ? uninstall[1..close] : uninstall;
                    arguments = close > 0 ? uninstall[(close + 1)..].Trim() : "";
                }
                else
                {
                    var spaceIdx = uninstall.IndexOf(' ');
                    if (spaceIdx > 0)
                    {
                        exe = uninstall[..spaceIdx];
                        arguments = uninstall[(spaceIdx + 1)..];
                    }
                    else
                    {
                        exe = uninstall;
                        arguments = "";
                    }
                }

                var psi = new System.Diagnostics.ProcessStartInfo(exe, arguments)
                { UseShellExecute = true };
                System.Diagnostics.Process.Start(psi);
            }
        }
        catch (Exception ex)
        {
            LogService.Warn($"제거 실행 실패 [{app.Name}]: {ex.Message}");
        }

        return Task.CompletedTask;
    }

    // ─── 내부 ─────────────────────────────────────────────────────────────────

    private static string BuildSubtitle(InstalledApp a)
    {
        var parts = new List<string>();
        if (!string.IsNullOrWhiteSpace(a.Publisher))  parts.Add(a.Publisher!);
        if (!string.IsNullOrWhiteSpace(a.Version))    parts.Add($"v{a.Version}");
        if (!string.IsNullOrWhiteSpace(a.InstallDate)) parts.Add(a.InstallDate!);
        parts.Add("Enter로 제거");
        return string.Join("  ·  ", parts);
    }

    private static List<InstalledApp> GetInstalledApps()
    {
        if (_cache.HasValue && (DateTime.Now - _cache.Value.At) < CacheTtl)
            return _cache.Value.Apps;

        var result = new List<InstalledApp>();
        var seen   = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        foreach (var root in new[] { Registry.LocalMachine, Registry.CurrentUser })
        {
            foreach (var regPath in _regPaths)
            {
                try
                {
                    using var key = root.OpenSubKey(regPath);
                    if (key == null) continue;

                    foreach (var subName in key.GetSubKeyNames())
                    {
                        try
                        {
                            using var sub = key.OpenSubKey(subName);
                            if (sub == null) continue;

                            var name      = sub.GetValue("DisplayName") as string;
                            var uninstall = sub.GetValue("UninstallString") as string;

                            // 이름·제거 문자열 없으면 스킵, 시스템 업데이트/패치 스킵
                            if (string.IsNullOrWhiteSpace(name))         continue;
                            if (string.IsNullOrWhiteSpace(uninstall))     continue;
                            if (name.StartsWith("KB", StringComparison.OrdinalIgnoreCase) &&
                                name.Length < 12)                          continue;
                            if (!seen.Add(name))                           continue;

                            var systemComponent = sub.GetValue("SystemComponent");
                            if (systemComponent is int sc && sc == 1)      continue;

                            result.Add(new InstalledApp
                            {
                                Name          = name,
                                UninstallString = uninstall,
                                Publisher     = sub.GetValue("Publisher")    as string,
                                Version       = sub.GetValue("DisplayVersion") as string,
                                InstallDate   = FormatDate(sub.GetValue("InstallDate") as string),
                            });
                        }
                        catch (Exception) { /* 개별 키 읽기 실패 무시 */ }
                    }
                }
                catch (Exception) { /* 레지스트리 접근 실패 무시 */ }
            }
        }

        result.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
        _cache = (DateTime.Now, result);
        return result;
    }

    private static string? FormatDate(string? raw)
    {
        // InstallDate 형식: "20230615" → "2023-06-15"
        if (raw?.Length == 8 &&
            int.TryParse(raw, out _))
        {
            return $"{raw[..4]}-{raw[4..6]}-{raw[6..8]}";
        }
        return null;
    }
}

internal sealed class InstalledApp
{
    public string  Name           { get; init; } = "";
    public string? UninstallString { get; init; }
    public string? Publisher       { get; init; }
    public string? Version         { get; init; }
    public string? InstallDate     { get; init; }
}
