using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Sta;

/// <summary>
///     SafeHandle-style lifetime wrapper for an <c>LMXProxyServer</c> COM connection. Per Task B.3
///     + decision #65: <see cref="ReleaseHandle"/> must call <c>Marshal.ReleaseComObject</c> until
///     refcount = 0, then <c>UnregisterProxy</c>. The finalizer runs as a
///     <see cref="CriticalFinalizerObject"/> to honor AppDomain-unload ordering.
/// </summary>
/// <remarks>
///     This scaffold accepts any RCW (tagged as <see cref="object"/>) so we can unit-test the
///     release logic with a mock. The concrete wiring to <c>ArchestrA.MxAccess.LMXProxyServer</c>
///     lands when the actual Galaxy code moves over (the part deferred to the parity gate).
/// </remarks>
public sealed class MxAccessHandle : SafeHandle
{
    private object? _comObject;
    private readonly Action<object>? _unregister;

    public MxAccessHandle(object comObject, Action<object>? unregister = null)
        : base(IntPtr.Zero, ownsHandle: true)
    {
        _comObject = comObject ?? throw new ArgumentNullException(nameof(comObject));
        _unregister = unregister;

        // The pointer value itself doesn't matter — we're wrapping an RCW, not a native handle.
        SetHandle(new IntPtr(1));
    }

    public override bool IsInvalid => handle == IntPtr.Zero;

    public object? RawComObject => _comObject;

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    protected override bool ReleaseHandle()
    {
        if (_comObject is null) return true;

        try { _unregister?.Invoke(_comObject); }
        catch { /* swallow — we're in finalizer/cleanup; log elsewhere */ }

        try
        {
            if (Marshal.IsComObject(_comObject))
            {
                while (Marshal.ReleaseComObject(_comObject) > 0) { /* loop until fully released */ }
            }
        }
        catch { /* swallow */ }

        _comObject = null;
        SetHandle(IntPtr.Zero);
        return true;
    }
}
