using System.Runtime.InteropServices;
using System.Runtime.Versioning;

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

/// <summary>
///     Confirms MXAccess COM server registration by resolving the
///     <c>LMXProxy.LMXProxyServer</c> ProgID to its CLSID, then checking that the CLSID's
///     32-bit <c>InprocServer32</c> entry points at a file that exists on disk.
/// </summary>
/// <remarks>
///     A common failure mode on partial installs: ProgID is registered but the CLSID
///     InprocServer32 DLL is missing (previous install uninstalled but registry orphan remains).
///     This probe surfaces that case with an actionable message instead of the
///     <c>0x80040154 REGDB_E_CLASSNOTREG</c> you'd see from a late COM activation failure.
/// </remarks>
public static class MxAccessComProbe
{
    public const string ProgId = "LMXProxy.LMXProxyServer";
    public const string VersionedProgId = "LMXProxy.LMXProxyServer.1";

    public static PrerequisiteCheck Check()
    {
        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            return new PrerequisiteCheck("com:LMXProxy", PrerequisiteCategory.MxAccessCom,
                PrerequisiteStatus.Skip, "COM registration probes only run on Windows.");
        }
        return CheckWindows();
    }

    [SupportedOSPlatform("windows")]
    private static PrerequisiteCheck CheckWindows()
    {
        try
        {
            var (clsid, dll) = RegistryProbe.ResolveProgIdToInproc(ProgId);
            if (clsid is null)
            {
                return new PrerequisiteCheck("com:LMXProxy", PrerequisiteCategory.MxAccessCom,
                    PrerequisiteStatus.Fail,
                    $"ProgID {ProgId} not registered — MXAccess COM server isn't installed. " +
                    $"Install System Platform's MXAccess component and re-run.");
            }

            if (string.IsNullOrWhiteSpace(dll))
            {
                return new PrerequisiteCheck("com:LMXProxy", PrerequisiteCategory.MxAccessCom,
                    PrerequisiteStatus.Fail,
                    $"ProgID {ProgId} → CLSID {clsid} but InprocServer32 is empty. " +
                    $"Registry is orphaned; re-register with: regsvr32 /s LmxProxy.dll (from an elevated cmd in the Framework bin dir).");
            }

            // Resolve the recorded path — sometimes registered as a bare filename that the COM
            // runtime resolves via the current process's DLL-search path. Accept either an
            // absolute path that exists, or a bare filename whose resolution we can't verify
            // without loading it (treat as Pass-with-note).
            if (Path.IsPathRooted(dll))
            {
                if (!File.Exists(dll))
                {
                    return new PrerequisiteCheck("com:LMXProxy", PrerequisiteCategory.MxAccessCom,
                        PrerequisiteStatus.Fail,
                        $"ProgID {ProgId} → CLSID {clsid} → InprocServer32 {dll}, but the file is missing. " +
                        $"Re-install the Framework or restore from backup.");
                }
                return new PrerequisiteCheck("com:LMXProxy", PrerequisiteCategory.MxAccessCom,
                    PrerequisiteStatus.Pass,
                    $"ProgID {ProgId} → {dll} (file exists).");
            }

            return new PrerequisiteCheck("com:LMXProxy", PrerequisiteCategory.MxAccessCom,
                PrerequisiteStatus.Pass,
                $"ProgID {ProgId} → {dll} (bare filename — relies on PATH resolution at COM activation time).");
        }
        catch (Exception ex)
        {
            return new PrerequisiteCheck("com:LMXProxy", PrerequisiteCategory.MxAccessCom,
                PrerequisiteStatus.Warn,
                $"Probe failed: {ex.GetType().Name}: {ex.Message}");
        }
    }

    /// <summary>
    ///     Warn when running as a 64-bit process — MXAccess COM activation will fail with
    ///     <c>0x80040154</c> regardless of registration state. The production drivers run net48
    ///     x86; xunit hosts run 64-bit by default so this often surfaces first.
    /// </summary>
    public static PrerequisiteCheck CheckProcessBitness()
    {
        if (Environment.Is64BitProcess)
        {
            return new PrerequisiteCheck("env:ProcessBitness", PrerequisiteCategory.Environment,
                PrerequisiteStatus.Warn,
                "Test host is 64-bit. Direct MXAccess COM activation would fail with REGDB_E_CLASSNOTREG (0x80040154); " +
                "the production driver workaround is to run Galaxy.Host as a 32-bit process. Tests that only " +
                "talk to the Host service over the named pipe aren't affected.");
        }
        return new PrerequisiteCheck("env:ProcessBitness", PrerequisiteCategory.Environment,
            PrerequisiteStatus.Pass, "Test host is 32-bit.");
    }
}
