using System.Diagnostics;
using System.IO;

namespace NexusShell.Services;

public sealed class NexusUpdateStopResult
{
    public bool Success { get; init; }
    public IReadOnlyList<string> StillRunning { get; init; } = [];
    public string? ErrorMessage { get; init; }
}

/// <summary>
/// Stops Nexus-owned processes before an in-place POC update. Never kills unrelated dotnet hosts.
/// </summary>
public static class NexusUpdateProcessStopper
{
    private static readonly string[] NexusProcessNames =
    [
        "NexusShell",
        "NexusIdentity.Api",
    ];

    public static TimeSpan DefaultGracePeriod { get; } = TimeSpan.FromSeconds(8);

    public static NexusUpdateStopResult StopForUpdate(
        string installRoot,
        bool includeCurrentShell = false,
        TimeSpan? gracePeriod = null)
    {
        var grace = gracePeriod ?? DefaultGracePeriod;
        var normalizedRoot = Path.GetFullPath(installRoot)
            .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

        IdentityHostService.StopRunning();

        var currentPid = includeCurrentShell ? -1 : Environment.ProcessId;
        var stopped = new List<string>();

        foreach (var name in NexusProcessNames)
        {
            foreach (var process in Process.GetProcessesByName(name))
            {
                using (process)
                {
                    if (!includeCurrentShell && process.Id == currentPid)
                    {
                        continue;
                    }

                    if (!BelongsToInstallRoot(process, normalizedRoot))
                    {
                        continue;
                    }

                    TryGracefulStop(process, grace);
                    stopped.Add($"{name}:{process.Id}");
                }
            }
        }

        StopFromPidFile(normalizedRoot, currentPid, grace);

        var stillRunning = FindRunningNexusProcesses(normalizedRoot, includeCurrentShell ? -1 : currentPid);
        if (stillRunning.Count > 0)
        {
            return new NexusUpdateStopResult
            {
                Success = false,
                StillRunning = stillRunning,
                ErrorMessage =
                    "Nexus-processer kører stadig og blokerer opdateringen: " +
                    string.Join(", ", stillRunning),
            };
        }

        UpdateLogWriter.Info(
            $"Process stop complete under {normalizedRoot}: {string.Join(", ", stopped)}");
        return new NexusUpdateStopResult { Success = true };
    }

    private static void StopFromPidFile(string installRoot, int excludePid, TimeSpan grace)
    {
        var pidFile = Path.Combine(installRoot, "logs", "nexus-poc.pids.json");
        if (!File.Exists(pidFile))
        {
            return;
        }

        try
        {
            var json = File.ReadAllText(pidFile);
            using var doc = System.Text.Json.JsonDocument.Parse(json);
            foreach (var prop in doc.RootElement.EnumerateObject())
            {
                if (!prop.Name.EndsWith("Pid", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                if (!prop.Value.TryGetInt32(out var pid) || pid <= 0 || pid == excludePid)
                {
                    continue;
                }

                try
                {
                    using var proc = Process.GetProcessById(pid);
                    if (BelongsToInstallRoot(proc, installRoot))
                    {
                        TryGracefulStop(proc, grace);
                    }
                }
                catch
                {
                    // Process already exited.
                }
            }
        }
        catch
        {
            // Best effort.
        }
    }

    private static List<string> FindRunningNexusProcesses(string installRoot, int excludePid)
    {
        var running = new List<string>();
        foreach (var name in NexusProcessNames)
        {
            foreach (var process in Process.GetProcessesByName(name))
            {
                using (process)
                {
                    if (process.Id == excludePid)
                    {
                        continue;
                    }

                    if (BelongsToInstallRoot(process, installRoot))
                    {
                        running.Add($"{name} (PID {process.Id})");
                    }
                }
            }
        }

        return running;
    }

    private static bool BelongsToInstallRoot(Process process, string installRoot)
    {
        try
        {
            var modulePath = process.MainModule?.FileName;
            if (string.IsNullOrWhiteSpace(modulePath))
            {
                return false;
            }

            var full = Path.GetFullPath(modulePath);
            return full.StartsWith(installRoot + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)
                || string.Equals(Path.GetDirectoryName(full), installRoot, StringComparison.OrdinalIgnoreCase);
        }
        catch
        {
            return false;
        }
    }

    private static void TryGracefulStop(Process process, TimeSpan grace)
    {
        try
        {
            if (process.HasExited)
            {
                return;
            }

            if (process.CloseMainWindow())
            {
                process.WaitForExit((int)grace.TotalMilliseconds);
            }

            if (!process.HasExited)
            {
                process.Kill(entireProcessTree: true);
                process.WaitForExit(3000);
            }
        }
        catch
        {
            // Best effort — caller verifies running set.
        }
    }
}
