namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus;

/// <summary>
///     Raised when a Modbus transaction leaves the single-flight socket in an unknown /
///     desynchronized state — a per-op response timeout, or a framing violation (transaction-id
///     mismatch, truncated frame, RTU CRC mismatch, or RTU unit-address mismatch). Unlike a Modbus
///     <em>exception PDU</em> (a well-formed
///     protocol-level error where the socket is still coherent and the request must simply
///     propagate), a desync means bytes may still be in flight or half-read, so the socket is
///     unusable and MUST be torn down before this is thrown.
/// </summary>
/// <remarks>
///     Derives from <see cref="IOException"/> deliberately: the transport's existing
///     reconnect-and-retry classifier (<c>IsSocketLevelFailure</c>) already treats
///     <see cref="IOException"/> as socket-fatal, so with auto-reconnect enabled the transport
///     transparently reconnects and resends once; with auto-reconnect disabled the (already torn
///     down) socket is never reused and this surfaces to the driver's status mapping as a
///     communication error.
/// </remarks>
public sealed class ModbusTransportDesyncException : IOException
{
    /// <summary>The reason the socket was classified desynchronized.</summary>
    public enum DesyncReason
    {
        /// <summary>The per-op deadline (linked <c>CancelAfter</c>) fired — the response never arrived in time.</summary>
        Timeout,

        /// <summary>The response MBAP transaction id did not match the request's.</summary>
        TxIdMismatch,

        /// <summary>
        ///     The frame ended before it was complete — the MBAP length field was below the mandatory
        ///     minimum (TCP), or the stream closed mid-frame while reading a length-less RTU frame.
        /// </summary>
        TruncatedFrame,

        /// <summary>The RTU frame's trailing CRC-16 did not validate over the received bytes.</summary>
        CrcMismatch,

        /// <summary>
        ///     A CRC-valid RTU frame carried a slave/unit address other than the one addressed — on an
        ///     RS-485 multi-drop bus, a reply from the wrong slave.
        /// </summary>
        UnitMismatch,
    }

    /// <summary>Gets the reason the socket was classified desynchronized.</summary>
    public DesyncReason Reason { get; }

    /// <summary>Initializes a new instance of the <see cref="ModbusTransportDesyncException"/> class.</summary>
    /// <param name="reason">The desync reason.</param>
    /// <param name="message">A human-readable description.</param>
    /// <param name="inner">The originating exception, if any.</param>
    public ModbusTransportDesyncException(DesyncReason reason, string message, Exception? inner = null)
        : base(message, inner)
    {
        Reason = reason;
    }
}
