using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Microsoft.Win32;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.TestSupport.Probes;

/// <summary>
///     Reads HKLM registry keys to confirm ArchestrA Framework / System Platform install
///     markers. Matches the registered paths documented in
///     <c>docs/v2/implementation/</c> — System Platform is 32-bit so keys live under
///     <c>HKLM\SOFTWARE\WOW6432Node\ArchestrA\...</c>.
/// </summary>
public static class RegistryProbe
{
    // Canonical install roots per the research on our dev box (System Platform 2020 R2).
    public const string ArchestrARootKey = @"SOFTWARE\WOW6432Node\ArchestrA";
    public const string FrameworkKey = @"SOFTWARE\WOW6432Node\ArchestrA\Framework";
    public const string PlatformKey = @"SOFTWARE\WOW6432Node\ArchestrA\Framework\Platform";
    public const string MsiInstallKey = @"SOFTWARE\WOW6432Node\ArchestrA\MSIInstall";

    public static PrerequisiteCheck CheckFrameworkInstalled()
    {
        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
                PrerequisiteStatus.Skip, "Registry probes only run on Windows.");
        }
        return FrameworkInstalledWindows();
    }

    public static PrerequisiteCheck CheckPlatformDeployed()
    {
        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            return new PrerequisiteCheck("registry:ArchestrA.Platform", PrerequisiteCategory.AvevaInstall,
                PrerequisiteStatus.Skip, "Registry probes only run on Windows.");
        }
        return PlatformDeployedWindows();
    }

    public static PrerequisiteCheck CheckRebootPending()
    {
        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            return new PrerequisiteCheck("registry:ArchestrA.RebootPending", PrerequisiteCategory.AvevaInstall,
                PrerequisiteStatus.Skip, "Registry probes only run on Windows.");
        }
        return RebootPendingWindows();
    }

    [SupportedOSPlatform("windows")]
    private static PrerequisiteCheck FrameworkInstalledWindows()
    {
        try
        {
            using var key = Registry.LocalMachine.OpenSubKey(FrameworkKey);
            if (key is null)
            {
                return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
                    PrerequisiteStatus.Fail,
                    $"Missing {FrameworkKey} — ArchestrA Framework isn't installed. Install AVEVA System Platform from the setup media.");
            }

            var installPath = key.GetValue("InstallPath") as string;
            var rootPath = key.GetValue("RootPath") as string;
            if (string.IsNullOrWhiteSpace(installPath) || string.IsNullOrWhiteSpace(rootPath))
            {
                return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
                    PrerequisiteStatus.Warn,
                    $"Framework key exists but InstallPath/RootPath values missing — install may be incomplete.");
            }

            return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
                PrerequisiteStatus.Pass,
                $"Installed at {installPath} (RootPath {rootPath}).");
        }
        catch (Exception ex)
        {
            return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
                PrerequisiteStatus.Warn,
                $"Probe failed: {ex.GetType().Name}: {ex.Message}");
        }
    }

    [SupportedOSPlatform("windows")]
    private static PrerequisiteCheck PlatformDeployedWindows()
    {
        try
        {
            using var key = Registry.LocalMachine.OpenSubKey(PlatformKey);
            var pfeConfig = key?.GetValue("PfeConfigOptions") as string;
            if (string.IsNullOrWhiteSpace(pfeConfig))
            {
                return new PrerequisiteCheck("registry:ArchestrA.Platform.Deployed", PrerequisiteCategory.AvevaInstall,
                    PrerequisiteStatus.Warn,
                    $"No Platform object deployed locally (Platform\\PfeConfigOptions empty). MXAccess will connect but subscriptions will fail. Deploy a Platform from the IDE.");
            }

            // PfeConfigOptions format: "PlatformId=N,EngineId=N,EngineName=...,..."
            // A non-deployed state leaves PlatformId=0 or the key empty.
            if (pfeConfig.Contains("PlatformId=0,", StringComparison.OrdinalIgnoreCase))
            {
                return new PrerequisiteCheck("registry:ArchestrA.Platform.Deployed", PrerequisiteCategory.AvevaInstall,
                    PrerequisiteStatus.Warn,
                    $"Platform never deployed (PfeConfigOptions has PlatformId=0). Deploy a Platform from the IDE before running live tests.");
            }

            return new PrerequisiteCheck("registry:ArchestrA.Platform.Deployed", PrerequisiteCategory.AvevaInstall,
                PrerequisiteStatus.Pass,
                $"Platform deployed ({pfeConfig}).");
        }
        catch (Exception ex)
        {
            return new PrerequisiteCheck("registry:ArchestrA.Platform.Deployed", PrerequisiteCategory.AvevaInstall,
                PrerequisiteStatus.Warn,
                $"Probe failed: {ex.GetType().Name}: {ex.Message}");
        }
    }

    [SupportedOSPlatform("windows")]
    private static PrerequisiteCheck RebootPendingWindows()
    {
        try
        {
            using var key = Registry.LocalMachine.OpenSubKey(MsiInstallKey);
            var rebootRequired = key?.GetValue("RebootRequired") as string;
            if (string.Equals(rebootRequired, "True", StringComparison.OrdinalIgnoreCase))
            {
                return new PrerequisiteCheck("registry:ArchestrA.RebootPending", PrerequisiteCategory.AvevaInstall,
                    PrerequisiteStatus.Warn,
                    "An ArchestrA patch has been installed but the machine hasn't rebooted. Post-patch behavior is undefined until a reboot.");
            }
            return new PrerequisiteCheck("registry:ArchestrA.RebootPending", PrerequisiteCategory.AvevaInstall,
                PrerequisiteStatus.Pass,
                "No pending reboot flagged.");
        }
        catch (Exception ex)
        {
            return new PrerequisiteCheck("registry:ArchestrA.RebootPending", PrerequisiteCategory.AvevaInstall,
                PrerequisiteStatus.Warn,
                $"Probe failed: {ex.GetType().Name}: {ex.Message}");
        }
    }

    /// <summary>
    ///     Read the registered <see cref="ComProgIdCheck"/> CLSID for the given ProgID and
    ///     resolve the 32-bit <c>InprocServer32</c> file path. Returns null when either is missing.
    /// </summary>
    [SupportedOSPlatform("windows")]
    internal static (string? Clsid, string? InprocDllPath) ResolveProgIdToInproc(string progId)
    {
        using var progIdKey = Registry.ClassesRoot.OpenSubKey($@"{progId}\CLSID");
        var clsid = progIdKey?.GetValue(null) as string;
        if (string.IsNullOrWhiteSpace(clsid)) return (null, null);

        // 32-bit COM server under Wow6432Node\CLSID\{guid}\InprocServer32 default value.
        using var inproc = Registry.LocalMachine.OpenSubKey(
            $@"SOFTWARE\Classes\WOW6432Node\CLSID\{clsid}\InprocServer32");
        var dll = inproc?.GetValue(null) as string;
        return (clsid, dll);
    }
}
