using System;
using ArchestrA.MxAccess;

namespace ZB.MOM.WW.MxGateway.Worker.MxAccess;

/// <summary>
/// Adapter exposing MXAccess COM object methods through the <see cref="IMxAccessServer"/>
/// interface.
/// </summary>
/// <remarks>
/// The supplied object must implement the typed MXAccess COM interface contract.
/// In production it is the <c>LMXProxyServerClass</c> RCW, which implements
/// <see cref="ILMXProxyServer"/> / <see cref="ILMXProxyServer3"/> /
/// <see cref="ILMXProxyServer4"/>. Tests substitute a typed fake that
/// implements <see cref="IMxAccessServer"/> directly. The earlier late-bound
/// <c>Type.InvokeMember</c> reflection fallback was removed: it bypassed the
/// typed interface contract, boxed value-type handles on every call, and only
/// ever served test doubles — a typed fake is the supported test seam now.
/// </remarks>
public sealed class MxAccessComServer : IMxAccessServer
{
    private readonly object mxAccessComObject;

    /// <summary>
    /// Initializes the adapter with the MXAccess COM object.
    /// </summary>
    /// <param name="mxAccessComObject">
    /// MXAccess COM object instance. Must implement either the typed
    /// <see cref="ILMXProxyServer"/> COM interface family (production) or
    /// <see cref="IMxAccessServer"/> directly (test fakes).
    /// </param>
    public MxAccessComServer(object mxAccessComObject)
    {
        this.mxAccessComObject = mxAccessComObject ?? throw new ArgumentNullException(nameof(mxAccessComObject));
    }

    /// <inheritdoc />
    public int Register(string clientName)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            return typedFake.Register(clientName);
        }

        return AsProxyServer().Register(clientName);
    }

    /// <inheritdoc />
    public void Unregister(int serverHandle)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.Unregister(serverHandle);
            return;
        }

        AsProxyServer().Unregister(serverHandle);
    }

    /// <inheritdoc />
    public int AddItem(
        int serverHandle,
        string itemDefinition)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            return typedFake.AddItem(serverHandle, itemDefinition);
        }

        return AsProxyServer().AddItem(serverHandle, itemDefinition);
    }

    /// <inheritdoc />
    public int AddItem2(
        int serverHandle,
        string itemDefinition,
        string itemContext)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            return typedFake.AddItem2(serverHandle, itemDefinition, itemContext);
        }

        return AsProxyServer3().AddItem2(serverHandle, itemDefinition, itemContext);
    }

    /// <inheritdoc />
    public void RemoveItem(
        int serverHandle,
        int itemHandle)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.RemoveItem(serverHandle, itemHandle);
            return;
        }

        AsProxyServer().RemoveItem(serverHandle, itemHandle);
    }

    /// <inheritdoc />
    public void Advise(
        int serverHandle,
        int itemHandle)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.Advise(serverHandle, itemHandle);
            return;
        }

        AsProxyServer().Advise(serverHandle, itemHandle);
    }

    /// <inheritdoc />
    public void UnAdvise(
        int serverHandle,
        int itemHandle)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.UnAdvise(serverHandle, itemHandle);
            return;
        }

        AsProxyServer().UnAdvise(serverHandle, itemHandle);
    }

    /// <inheritdoc />
    public void AdviseSupervisory(
        int serverHandle,
        int itemHandle)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.AdviseSupervisory(serverHandle, itemHandle);
            return;
        }

        AsProxyServer4().AdviseSupervisory(serverHandle, itemHandle);
    }

    /// <inheritdoc />
    public object Suspend(
        int serverHandle,
        int itemHandle)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            return typedFake.Suspend(serverHandle, itemHandle);
        }

        AsProxyServer4().Suspend(serverHandle, itemHandle, out MxStatus status);
        return status;
    }

    /// <inheritdoc />
    public object Activate(
        int serverHandle,
        int itemHandle)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            return typedFake.Activate(serverHandle, itemHandle);
        }

        AsProxyServer4().Activate(serverHandle, itemHandle, out MxStatus status);
        return status;
    }

    /// <inheritdoc />
    public int AuthenticateUser(
        int serverHandle,
        string verifyUser,
        string verifyUserPassword)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            return typedFake.AuthenticateUser(serverHandle, verifyUser, verifyUserPassword);
        }

        return AsProxyServer().AuthenticateUser(serverHandle, verifyUser, verifyUserPassword);
    }

    /// <inheritdoc />
    public int ArchestrAUserToId(
        int serverHandle,
        string userIdGuid)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            return typedFake.ArchestrAUserToId(serverHandle, userIdGuid);
        }

        return AsProxyServer2().ArchestrAUserToId(serverHandle, userIdGuid);
    }

    /// <inheritdoc />
    public int AddBufferedItem(
        int serverHandle,
        string itemDefinition,
        string itemContext)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            return typedFake.AddBufferedItem(serverHandle, itemDefinition, itemContext);
        }

        return AsProxyServer5().AddBufferedItem(serverHandle, itemDefinition, itemContext);
    }

    /// <inheritdoc />
    public void SetBufferedUpdateInterval(
        int serverHandle,
        int updateIntervalMilliseconds)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.SetBufferedUpdateInterval(serverHandle, updateIntervalMilliseconds);
            return;
        }

        AsProxyServer5().SetBufferedUpdateInterval(serverHandle, updateIntervalMilliseconds);
    }

    /// <inheritdoc />
    public void Write(
        int serverHandle,
        int itemHandle,
        object? value,
        int userId)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.Write(serverHandle, itemHandle, value, userId);
            return;
        }

        AsProxyServer().Write(serverHandle, itemHandle, value!, userId);
    }

    /// <inheritdoc />
    public void Write2(
        int serverHandle,
        int itemHandle,
        object? value,
        object? timestamp,
        int userId)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.Write2(serverHandle, itemHandle, value, timestamp, userId);
            return;
        }

        AsProxyServer4().Write2(serverHandle, itemHandle, value!, timestamp!, userId);
    }

    /// <inheritdoc />
    public void WriteSecured(
        int serverHandle,
        int itemHandle,
        int currentUserId,
        int verifierUserId,
        object? value)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.WriteSecured(serverHandle, itemHandle, currentUserId, verifierUserId, value);
            return;
        }

        AsProxyServer().WriteSecured(serverHandle, itemHandle, currentUserId, verifierUserId, value!);
    }

    /// <inheritdoc />
    public void WriteSecured2(
        int serverHandle,
        int itemHandle,
        int currentUserId,
        int verifierUserId,
        object? value,
        object? timestamp)
    {
        if (mxAccessComObject is IMxAccessServer typedFake)
        {
            typedFake.WriteSecured2(serverHandle, itemHandle, currentUserId, verifierUserId, value, timestamp);
            return;
        }

        AsProxyServer4().WriteSecured2(serverHandle, itemHandle, currentUserId, verifierUserId, value!, timestamp!);
    }

    private ILMXProxyServer AsProxyServer()
    {
        return mxAccessComObject as ILMXProxyServer
            ?? throw new InvalidOperationException(
                $"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
                + $"{nameof(ILMXProxyServer)} or {nameof(IMxAccessServer)}.");
    }

    private ILMXProxyServer2 AsProxyServer2()
    {
        return mxAccessComObject as ILMXProxyServer2
            ?? throw new InvalidOperationException(
                $"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
                + $"{nameof(ILMXProxyServer2)} or {nameof(IMxAccessServer)}.");
    }

    private ILMXProxyServer3 AsProxyServer3()
    {
        return mxAccessComObject as ILMXProxyServer3
            ?? throw new InvalidOperationException(
                $"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
                + $"{nameof(ILMXProxyServer3)} or {nameof(IMxAccessServer)}.");
    }

    private ILMXProxyServer4 AsProxyServer4()
    {
        return mxAccessComObject as ILMXProxyServer4
            ?? throw new InvalidOperationException(
                $"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
                + $"{nameof(ILMXProxyServer4)} or {nameof(IMxAccessServer)}.");
    }

    private ILMXProxyServer5 AsProxyServer5()
    {
        return mxAccessComObject as ILMXProxyServer5
            ?? throw new InvalidOperationException(
                $"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
                + $"{nameof(ILMXProxyServer5)} or {nameof(IMxAccessServer)}.");
    }
}
