using System.Diagnostics;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// 실행 중인 프로세스를 검색하고 종료합니다. "kill " 프리픽스로 사용합니다.
/// 예: kill chrome     → Chrome 프로세스 목록 표시 후 선택하면 종료
///     kill            → 현재 실행 중인 모든 사용자 프로세스 표시
/// </summary>
public class ProcessHandler : IActionHandler
{
    public string? Prefix => "kill ";   // 뒤에 공백 포함 — 오탐 방지

    public PluginMetadata Metadata => new(
        "ProcessKiller",
        "프로세스 종료 — kill 뒤에 프로세스명 입력",
        "1.0",
        "AX");

    // 시스템 핵심 프로세스 보호 목록 (종료 방지)
    private static readonly HashSet<string> ProtectedProcesses = new(StringComparer.OrdinalIgnoreCase)
    {
        "system", "smss", "csrss", "wininit", "winlogon", "services", "lsass",
        "svchost", "explorer", "dwm", "fontdrvhost", "spoolsv", "registry",
    };

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(query))
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(
            [
                new LauncherItem(
                    "종료할 프로세스명을 입력하세요",
                    "예: kill chrome  ·  kill notepad  ·  kill explorer",
                    null, null, Symbol: Symbols.Power)
            ]);
        }

        var q = query.Trim().ToLowerInvariant();

        try
        {
            var processes = Process.GetProcesses()
                .Where(p =>
                    !ProtectedProcesses.Contains(p.ProcessName) &&
                    p.ProcessName.ToLowerInvariant().Contains(q))
                .OrderBy(p => p.ProcessName)
                .Take(12)
                .ToList();

            if (processes.Count == 0)
            {
                return Task.FromResult<IEnumerable<LauncherItem>>(
                [
                    new LauncherItem(
                        $"'{query}' 프로세스를 찾을 수 없습니다",
                        "실행 중인 프로세스가 없거나 이름이 다릅니다",
                        null, null, Symbol: Symbols.Warning)
                ]);
            }

            // 같은 이름의 프로세스 묶기
            var grouped = processes
                .GroupBy(p => p.ProcessName, StringComparer.OrdinalIgnoreCase)
                .Select(g =>
                {
                    var pids = g.Select(p => p.Id).ToList();
                    var memMb = g.Sum(p =>
                    {
                        try { return p.WorkingSet64 / 1024 / 1024; }
                        catch (Exception) { return 0L; }
                    });
                    var title = g.Count() > 1
                        ? $"{g.Key}  ({g.Count()}개 인스턴스)"
                        : g.Key;
                    var subtitle = $"PID: {string.Join(", ", pids)}  ·  메모리: {memMb} MB  ·  Enter로 종료";

                    return new LauncherItem(
                        title,
                        subtitle,
                        null,
                        new ProcessKillData(g.Key, pids),
                        Symbol: Symbols.Power);
                })
                .ToList();

            return Task.FromResult<IEnumerable<LauncherItem>>(grouped);
        }
        catch (Exception ex)
        {
            LogService.Warn($"프로세스 목록 조회 실패: {ex.Message}");
            return Task.FromResult<IEnumerable<LauncherItem>>(
            [
                new LauncherItem("프로세스 목록 조회 실패", ex.Message, null, null, Symbol: Symbols.Error)
            ]);
        }
    }

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

        int killed = 0, failed = 0;
        foreach (var pid in data.Pids)
        {
            try
            {
                var proc = Process.GetProcessById(pid);
                proc.Kill(entireProcessTree: false);
                killed++;
            }
            catch (Exception)
            {
                failed++;
            }
        }

        LogService.Info($"프로세스 종료: {data.Name} — {killed}개 성공, {failed}개 실패");
        return Task.CompletedTask;
    }

    private record ProcessKillData(string Name, List<int> Pids);
}
