using System.Diagnostics;
using System.IO;

namespace NexusShell.Services;

public sealed class NexusFileLockInfo
{
    public required string FilePath { get; init; }
    public string? LockingProcess { get; init; }
}

public sealed class NexusFileLockCheckResult
{
    public bool CanProceed { get; init; }
    public IReadOnlyList<NexusFileLockInfo> LockedFiles { get; init; } = [];
    public string? SummaryMessage { get; init; }
}

/// <summary>
/// Preflight check that install targets are not locked before apply.
/// </summary>
public static class NexusUpdateFileLockChecker
{
    private static readonly string[] RelativeTargets =
    [
        "identity/e_sqlite3.dll",
        "identity/NexusIdentity.Api.exe",
        "identity/SQLitePCLRaw.provider.e_sqlite3.dll",
        "shell/NexusShell.exe",
    ];

    public static NexusFileLockCheckResult CheckInstallLocks(string installRoot)
    {
        var locked = new List<NexusFileLockInfo>();
        foreach (var relative in RelativeTargets)
        {
            var fullPath = Path.Combine(installRoot, relative.Replace('/', Path.DirectorySeparatorChar));
            if (!File.Exists(fullPath))
            {
                continue;
            }

            if (!CanOpenExclusively(fullPath))
            {
                locked.Add(new NexusFileLockInfo
                {
                    FilePath = relative,
                    LockingProcess = TryIdentifyLockingProcess(installRoot, fullPath),
                });
            }
        }

        if (locked.Count == 0)
        {
            return new NexusFileLockCheckResult { CanProceed = true };
        }

        var first = locked[0];
        var detail = first.LockingProcess is not null
            ? $"{first.FilePath} (låst af {first.LockingProcess})"
            : first.FilePath;

        return new NexusFileLockCheckResult
        {
            CanProceed = false,
            LockedFiles = locked,
            SummaryMessage =
                $"Installationsfiler er låst og kan ikke overskrives: {detail}. " +
                "Stop Nexus Shell og Identity, og prøv igen.",
        };
    }

    private static bool CanOpenExclusively(string path)
    {
        try
        {
            using var stream = new FileStream(
                path,
                FileMode.Open,
                FileAccess.ReadWrite,
                FileShare.None);
            return true;
        }
        catch (IOException)
        {
            return false;
        }
        catch (UnauthorizedAccessException)
        {
            return false;
        }
    }

    private static string? TryIdentifyLockingProcess(string installRoot, string lockedFile)
    {
        var fileName = Path.GetFileName(lockedFile);
        foreach (var name in new[] { "NexusIdentity.Api", "NexusShell" })
        {
            foreach (var process in Process.GetProcessesByName(name))
            {
                using (process)
                {
                    try
                    {
                        var modulePath = process.MainModule?.FileName;
                        if (string.IsNullOrWhiteSpace(modulePath))
                        {
                            continue;
                        }

                        var full = Path.GetFullPath(modulePath);
                        if (!full.StartsWith(
                                Path.GetFullPath(installRoot),
                                StringComparison.OrdinalIgnoreCase))
                        {
                            continue;
                        }

                        if (string.Equals(Path.GetFileName(modulePath), fileName, StringComparison.OrdinalIgnoreCase)
                            || fileName.StartsWith("e_sqlite3", StringComparison.OrdinalIgnoreCase)
                            || fileName.Contains("sqlite", StringComparison.OrdinalIgnoreCase))
                        {
                            return $"{name} (PID {process.Id})";
                        }
                    }
                    catch
                    {
                        // Access denied for some processes.
                    }
                }
            }
        }

        return null;
    }
}
