using System;
using System.Threading;
using System.Threading.Tasks;
using MessagePack;
using Serilog;
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Backend;
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Shared;
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Shared.Contracts;

namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Ipc;

/// <summary>
///     Real FOCAS frame handler. Deserializes each request DTO, delegates to
///     <see cref="IFocasBackend"/>, re-serializes the response. The backend owns the
///     Fwlib32 handle + STA thread — the handler is pure dispatch.
/// </summary>
public sealed class FwlibFrameHandler : IFrameHandler
{
    private readonly IFocasBackend _backend;
    private readonly ILogger _logger;

    public FwlibFrameHandler(IFocasBackend backend, ILogger logger)
    {
        _backend = backend ?? throw new ArgumentNullException(nameof(backend));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task HandleAsync(FocasMessageKind kind, byte[] body, FrameWriter writer, CancellationToken ct)
    {
        try
        {
            switch (kind)
            {
                case FocasMessageKind.Heartbeat:
                {
                    var hb = MessagePackSerializer.Deserialize<Heartbeat>(body);
                    await writer.WriteAsync(FocasMessageKind.HeartbeatAck,
                        new HeartbeatAck
                        {
                            MonotonicTicks = hb.MonotonicTicks,
                            HostUtcUnixMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                        }, ct).ConfigureAwait(false);
                    return;
                }

                case FocasMessageKind.OpenSessionRequest:
                {
                    var req = MessagePackSerializer.Deserialize<OpenSessionRequest>(body);
                    var resp = await _backend.OpenSessionAsync(req, ct).ConfigureAwait(false);
                    await writer.WriteAsync(FocasMessageKind.OpenSessionResponse, resp, ct).ConfigureAwait(false);
                    return;
                }

                case FocasMessageKind.CloseSessionRequest:
                {
                    var req = MessagePackSerializer.Deserialize<CloseSessionRequest>(body);
                    await _backend.CloseSessionAsync(req, ct).ConfigureAwait(false);
                    return;
                }

                case FocasMessageKind.ReadRequest:
                {
                    var req = MessagePackSerializer.Deserialize<ReadRequest>(body);
                    var resp = await _backend.ReadAsync(req, ct).ConfigureAwait(false);
                    await writer.WriteAsync(FocasMessageKind.ReadResponse, resp, ct).ConfigureAwait(false);
                    return;
                }

                case FocasMessageKind.WriteRequest:
                {
                    var req = MessagePackSerializer.Deserialize<WriteRequest>(body);
                    var resp = await _backend.WriteAsync(req, ct).ConfigureAwait(false);
                    await writer.WriteAsync(FocasMessageKind.WriteResponse, resp, ct).ConfigureAwait(false);
                    return;
                }

                case FocasMessageKind.PmcBitWriteRequest:
                {
                    var req = MessagePackSerializer.Deserialize<PmcBitWriteRequest>(body);
                    var resp = await _backend.PmcBitWriteAsync(req, ct).ConfigureAwait(false);
                    await writer.WriteAsync(FocasMessageKind.PmcBitWriteResponse, resp, ct).ConfigureAwait(false);
                    return;
                }

                case FocasMessageKind.ProbeRequest:
                {
                    var req = MessagePackSerializer.Deserialize<ProbeRequest>(body);
                    var resp = await _backend.ProbeAsync(req, ct).ConfigureAwait(false);
                    await writer.WriteAsync(FocasMessageKind.ProbeResponse, resp, ct).ConfigureAwait(false);
                    return;
                }

                default:
                    await writer.WriteAsync(FocasMessageKind.ErrorResponse,
                        new ErrorResponse { Code = "unknown-kind", Message = $"Kind {kind} is not handled by the Host" },
                        ct).ConfigureAwait(false);
                    return;
            }
        }
        catch (OperationCanceledException) { throw; }
        catch (Exception ex)
        {
            _logger.Error(ex, "FwlibFrameHandler error processing {Kind}", kind);
            await writer.WriteAsync(FocasMessageKind.ErrorResponse,
                new ErrorResponse { Code = "backend-exception", Message = ex.Message },
                ct).ConfigureAwait(false);
        }
    }

    public IDisposable AttachConnection(FrameWriter writer) => IFrameHandler.NoopAttachment.Instance;
}
