using System;
using ArchestrA.MxAccess;
using ZB.MOM.WW.LmxProxy.Host.Domain;

namespace ZB.MOM.WW.LmxProxy.Host.Implementation
{
    /// <summary>
    ///     Event handlers for MxAccessClient to process data changes, write completions, and operation completions.
    /// </summary>
    public sealed partial class MxAccessClient
    {
        /// <summary>
        ///     Handles data change events from the MxAccess server.
        /// </summary>
        /// <param name="hLMXServerHandle">Server handle.</param>
        /// <param name="phItemHandle">Item handle.</param>
        /// <param name="pvItemValue">Item value.</param>
        /// <param name="pwItemQuality">Item quality code.</param>
        /// <param name="pftItemTimeStamp">Item timestamp.</param>
        /// <param name="ItemStatus">Status array.</param>
        private void OnDataChange(int hLMXServerHandle, int phItemHandle, object pvItemValue,
            int pwItemQuality, object pftItemTimeStamp, ref MXSTATUS_PROXY[] ItemStatus)
        {
            try
            {
                if (!_subscriptionsByHandle.TryGetValue(phItemHandle, out SubscriptionInfo? subscription))
                {
                    return;
                }

                // Convert quality from integer
                Quality quality = ConvertQuality(pwItemQuality);
                DateTime timestamp = ConvertTimestamp(pftItemTimeStamp);
                var vtq = new Vtq(pvItemValue, timestamp, quality);

                // Invoke callback
                subscription.Callback?.Invoke(subscription.Address, vtq);
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Error processing data change for handle {Handle}", phItemHandle);
            }
        }

        /// <summary>
        ///     Handles write completion events from the MxAccess server.
        /// </summary>
        /// <param name="hLMXServerHandle">Server handle.</param>
        /// <param name="phItemHandle">Item handle.</param>
        /// <param name="ItemStatus">Status array.</param>
        private void OnWriteComplete(int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus)
        {
            try
            {
                WriteOperation? writeOp;

                lock (_lock)
                {
                    if (_pendingWrites.TryGetValue(phItemHandle, out writeOp))
                    {
                        _pendingWrites.Remove(phItemHandle);
                    }
                }

                if (writeOp != null)
                {
                    try
                    {
                        if (ItemStatus is { Length: > 0 })
                        {
                            var status = ItemStatus[0];
                            if (status.success == 0)
                            {
                                string errorMsg = GetWriteErrorMessage(status.detail);
                                Logger.Warning(
                                    "Write failed for {Address} (handle {Handle}): {Error} (Category={Category}, Detail={Detail})",
                                    writeOp.Address, phItemHandle, errorMsg, status.category, status.detail);

                                writeOp.CompletionSource.TrySetException(new InvalidOperationException(
                                    $"Write failed: {errorMsg}"));
                            }
                            else
                            {
                                Logger.Debug("Write completed successfully for {Address} (handle {Handle})",
                                    writeOp.Address, phItemHandle);
                                writeOp.CompletionSource.TrySetResult(true);
                            }
                        }
                        else
                        {
                            Logger.Debug("Write completed for {Address} (handle {Handle}) with no status",
                                writeOp.Address, phItemHandle);
                            writeOp.CompletionSource.TrySetResult(true);
                        }
                    }
                    finally
                    {
                        // Clean up the item after write completes
                        lock (_lock)
                        {
                            if (_lmxProxy != null)
                            {
                                try
                                {
                                    _lmxProxy.UnAdvise(_connectionHandle, phItemHandle);
                                    _lmxProxy.RemoveItem(_connectionHandle, phItemHandle);
                                }
                                catch (Exception ex)
                                {
                                    Logger.Debug(ex, "Error cleaning up after write for handle {Handle}", phItemHandle);
                                }
                            }
                        }
                    }
                }
                else if (ItemStatus is { Length: > 0 })
                {
                    var status = ItemStatus[0];
                    if (status.success == 0)
                    {
                        Logger.Warning("Write failed for unknown handle {Handle}: Category={Category}, Detail={Detail}",
                            phItemHandle, status.category, status.detail);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Error processing write complete for handle {Handle}", phItemHandle);
            }
        }

        /// <summary>
        ///     Handles operation completion events from the MxAccess server.
        /// </summary>
        /// <param name="hLMXServerHandle">Server handle.</param>
        /// <param name="phItemHandle">Item handle.</param>
        /// <param name="ItemStatus">Status array.</param>
        private void OnOperationComplete(int hLMXServerHandle, int phItemHandle, ref MXSTATUS_PROXY[] ItemStatus)
        {
            // Log operation completion
            Logger.Debug("Operation complete for handle {Handle}", phItemHandle);
        }

        /// <summary>
        ///     Converts an integer MxAccess quality code to <see cref="Quality" />.
        /// </summary>
        /// <param name="mxQuality">The MxAccess quality code.</param>
        /// <returns>The corresponding <see cref="Quality" /> value.</returns>
        private Quality ConvertQuality(int mxQuality) => (Quality)mxQuality;

        /// <summary>
        ///     Converts a timestamp object to <see cref="DateTime" /> in UTC.
        /// </summary>
        /// <param name="timestamp">The timestamp object.</param>
        /// <returns>The UTC <see cref="DateTime" /> value.</returns>
        private DateTime ConvertTimestamp(object timestamp)
        {
            if (timestamp is DateTime dt)
            {
                return dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime();
            }

            return DateTime.UtcNow;
        }
    }
}
