using System.Diagnostics;

namespace NexusInstaller.Services;

internal sealed class ProcessStopResult
{
    public bool Success { get; init; }
    public bool Blocked { get; init; }
    public string Message { get; init; } = string.Empty;
    public IReadOnlyList<int> StoppedPids { get; init; } = [];
}

internal static class NexusProcessStopService
{
    private static readonly string[] NexusProcessNames = ["NexusShell", "NexusIdentity.Api", "NexusAgent"];

    public static async Task<ProcessStopResult> StopNexusProcessesAsync(
        string installRoot,
        IProgress<string>? log = null,
        CancellationToken cancellationToken = default)
    {
        var normalizedRoot = Path.GetFullPath(installRoot).TrimEnd('\\', '/');
        var stopped = new List<int>();
        var blocked = new List<string>();

        foreach (var name in NexusProcessNames)
        {
            foreach (var process in Process.GetProcessesByName(name))
            {
                using (process)
                {
                    if (!IsNexusProcessUnderRoot(process, normalizedRoot))
                    {
                        continue;
                    }

                    log?.Report($"Stopper {name} (PID {process.Id})…");
                    if (await TryStopProcessAsync(process, log, cancellationToken))
                    {
                        stopped.Add(process.Id);
                    }
                    else
                    {
                        blocked.Add($"{name} (PID {process.Id})");
                    }
                }
            }
        }

        if (blocked.Count > 0)
        {
            return new ProcessStopResult
            {
                Success = false,
                Blocked = true,
                Message = $"Kunne ikke stoppe: {string.Join(", ", blocked)}",
                StoppedPids = stopped,
            };
        }

        return new ProcessStopResult
        {
            Success = true,
            Message = stopped.Count == 0 ? "Ingen Nexus-processer kørte." : $"Stoppede {stopped.Count} proces(s).",
            StoppedPids = stopped,
        };
    }

    private static bool IsNexusProcessUnderRoot(Process process, string installRoot)
    {
        try
        {
            var path = process.MainModule?.FileName;
            if (string.IsNullOrWhiteSpace(path))
            {
                return process.ProcessName.StartsWith("Nexus", StringComparison.OrdinalIgnoreCase);
            }

            var full = Path.GetFullPath(path);
            if (full.StartsWith(installRoot, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }

            var localNexus = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                "NinjaNexus");

            return full.StartsWith(localNexus, StringComparison.OrdinalIgnoreCase);
        }
        catch
        {
            return process.ProcessName.StartsWith("Nexus", StringComparison.OrdinalIgnoreCase);
        }
    }

    private static async Task<bool> TryStopProcessAsync(
        Process process,
        IProgress<string>? log,
        CancellationToken cancellationToken)
    {
        try
        {
            if (process.HasExited)
            {
                return true;
            }

            process.CloseMainWindow();
            if (await WaitForExitAsync(process, 3000, cancellationToken))
            {
                return true;
            }

            process.Kill(entireProcessTree: true);
            return await WaitForExitAsync(process, 5000, cancellationToken);
        }
        catch (Exception ex)
        {
            log?.Report($"Stop fejlede PID {process.Id}: {ex.Message}");
            return false;
        }
    }

    private static async Task<bool> WaitForExitAsync(Process process, int ms, CancellationToken ct)
    {
        try
        {
            await process.WaitForExitAsync(ct).WaitAsync(TimeSpan.FromMilliseconds(ms), ct);
            return process.HasExited;
        }
        catch
        {
            return process.HasExited;
        }
    }
}
