using System;
using System.Runtime.InteropServices;
using ArchestrA.MxAccess;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.MxAccess;

/// <summary>
///     Concrete <see cref="IMxProxy"/> backed by a real <c>LMXProxyServer</c> COM object.
///     Port of v1 <c>MxProxyAdapter</c>. <strong>Must only be constructed on an STA thread</strong>
///     — the StaPump owns this instance.
/// </summary>
public sealed class MxProxyAdapter : IMxProxy, IDisposable
{
    private LMXProxyServer? _lmxProxy;

    public event MxDataChangeHandler? OnDataChange;
    public event MxWriteCompleteHandler? OnWriteComplete;

    public int Register(string clientName)
    {
        _lmxProxy = new LMXProxyServer();
        _lmxProxy.OnDataChange += ProxyOnDataChange;
        _lmxProxy.OnWriteComplete += ProxyOnWriteComplete;

        var handle = _lmxProxy.Register(clientName);
        if (handle <= 0)
            throw new InvalidOperationException($"LMXProxyServer.Register returned invalid handle: {handle}");
        return handle;
    }

    public void Unregister(int handle)
    {
        if (_lmxProxy is null) return;
        try
        {
            _lmxProxy.OnDataChange -= ProxyOnDataChange;
            _lmxProxy.OnWriteComplete -= ProxyOnWriteComplete;
            _lmxProxy.Unregister(handle);
        }
        finally
        {
            // ReleaseComObject loop until refcount = 0 — the Tier C SafeHandle wraps this in
            // production; here the lifetime is owned by the surrounding MxAccessHandle.
            while (Marshal.IsComObject(_lmxProxy) && Marshal.ReleaseComObject(_lmxProxy) > 0) { }
            _lmxProxy = null;
        }
    }

    public int AddItem(int handle, string address) => _lmxProxy!.AddItem(handle, address);

    public void RemoveItem(int handle, int itemHandle) => _lmxProxy!.RemoveItem(handle, itemHandle);

    public void AdviseSupervisory(int handle, int itemHandle) => _lmxProxy!.AdviseSupervisory(handle, itemHandle);

    public void UnAdviseSupervisory(int handle, int itemHandle) => _lmxProxy!.UnAdvise(handle, itemHandle);

    public void Write(int handle, int itemHandle, object value, int securityClassification) =>
        _lmxProxy!.Write(handle, itemHandle, value, securityClassification);

    private void ProxyOnDataChange(int hLMXServerHandle, int phItemHandle, object pvItemValue,
        int pwItemQuality, object pftItemTimeStamp, ref MXSTATUS_PROXY[] ItemStatus)
        => OnDataChange?.Invoke(hLMXServerHandle, phItemHandle, pvItemValue, pwItemQuality, pftItemTimeStamp, ref ItemStatus);

    private void ProxyOnWriteComplete(int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus)
        => OnWriteComplete?.Invoke(hLMXServerHandle, phItemHandle, ref ItemStatus);

    public void Dispose() => Unregister(0);
}
