using ZB.MOM.WW.MxGateway.Contracts.Proto;
using ProtoConditionState = ZB.MOM.WW.MxGateway.Contracts.Proto.AlarmConditionState;
using ProtoTransitionKind = ZB.MOM.WW.MxGateway.Contracts.Proto.AlarmTransitionKind;
// Alias the Commons alarm types so their simple names bind here, unambiguous
// against the colliding gateway proto enums above.
using NativeAlarmTransition = ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms.NativeAlarmTransition;
using AlarmConditionState = ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms.AlarmConditionState;
using AlarmTransitionKind = ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AlarmTransitionKind;
using AlarmShelveState = ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AlarmShelveState;

namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;

/// <summary>
/// Pure mapping from MxAccess Gateway alarm-feed proto messages to the
/// protocol-neutral <see cref="NativeAlarmTransition"/> shape. The gateway proto
/// enums (AlarmConditionState / AlarmTransitionKind) collide with the Commons
/// alarm enums, so they are aliased here. Unit-tested without a live gateway.
/// </summary>
public static class MxGatewayAlarmMapper
{
    /// <summary>Clamps the gateway severity onto the unified 0–1000 scale.</summary>
    /// <param name="severity">Raw gateway severity value to clamp.</param>
    /// <returns>The severity clamped to the range [0, 1000].</returns>
    public static int NormalizeSeverity(int severity) => Math.Clamp(severity, 0, 1000);

    /// <summary>Maps a gateway condition-state + severity to the unified condition.</summary>
    /// <param name="state">Gateway proto condition state to map.</param>
    /// <param name="severity">Raw gateway severity value; normalized to 0–1000.</param>
    /// <returns>The protocol-neutral <see cref="AlarmConditionState"/> equivalent.</returns>
    public static AlarmConditionState MapConditionState(ProtoConditionState state, int severity)
    {
        var (active, acked) = state switch
        {
            ProtoConditionState.Active => (true, false),
            ProtoConditionState.ActiveAcked => (true, true),
            ProtoConditionState.Inactive => (false, true),
            _ => (false, true)
        };
        return new AlarmConditionState(active, acked, Confirmed: null,
            Shelve: AlarmShelveState.Unshelved, Suppressed: false, Severity: NormalizeSeverity(severity));
    }

    /// <summary>Maps a gateway transition kind to the unified transition kind.</summary>
    /// <param name="kind">Gateway proto transition kind to map.</param>
    /// <returns>The protocol-neutral <see cref="AlarmTransitionKind"/> equivalent.</returns>
    public static AlarmTransitionKind MapKind(ProtoTransitionKind kind) => kind switch
    {
        ProtoTransitionKind.Raise => AlarmTransitionKind.Raise,
        ProtoTransitionKind.Acknowledge => AlarmTransitionKind.Acknowledge,
        ProtoTransitionKind.Clear => AlarmTransitionKind.Clear,
        ProtoTransitionKind.Retrigger => AlarmTransitionKind.Retrigger,
        _ => AlarmTransitionKind.StateChange
    };

    /// <summary>Derives the mirrored condition from a transition kind + severity.</summary>
    private static AlarmConditionState ConditionFromKind(ProtoTransitionKind kind, int severity)
    {
        var (active, acked) = kind switch
        {
            ProtoTransitionKind.Raise => (true, false),
            ProtoTransitionKind.Acknowledge => (true, true),
            ProtoTransitionKind.Retrigger => (true, false),
            ProtoTransitionKind.Clear => (false, true),
            _ => (false, true)
        };
        return new AlarmConditionState(active, acked, Confirmed: null,
            Shelve: AlarmShelveState.Unshelved, Suppressed: false, Severity: NormalizeSeverity(severity));
    }

    /// <summary>Maps a live <see cref="OnAlarmTransitionEvent"/> to a transition.</summary>
    /// <param name="body">The gateway alarm transition event proto message to map.</param>
    /// <returns>The protocol-neutral <see cref="NativeAlarmTransition"/>.</returns>
    public static NativeAlarmTransition MapTransition(OnAlarmTransitionEvent body) => new(
        SourceReference: body.AlarmFullReference,
        SourceObjectReference: body.SourceObjectReference,
        AlarmTypeName: body.AlarmTypeName,
        Kind: MapKind(body.TransitionKind),
        Condition: ConditionFromKind(body.TransitionKind, body.Severity),
        Category: body.Category,
        Description: body.Description,
        Message: body.Description,
        OperatorUser: body.OperatorUser,
        OperatorComment: body.OperatorComment,
        OriginalRaiseTime: body.OriginalRaiseTimestamp?.ToDateTimeOffset(),
        TransitionTime: body.TransitionTimestamp?.ToDateTimeOffset() ?? DateTimeOffset.UtcNow,
        CurrentValue: "",
        LimitValue: "");

    /// <summary>The end-of-snapshot sentinel transition (no condition payload).</summary>
    /// <returns>A <see cref="NativeAlarmTransition"/> with <c>AlarmTransitionKind.SnapshotComplete</c>.</returns>
    public static NativeAlarmTransition SnapshotComplete() => new(
        "", "", "", AlarmTransitionKind.SnapshotComplete,
        new AlarmConditionState(false, true, null, AlarmShelveState.Unshelved, false, 0),
        "", "", "", "", "", null, DateTimeOffset.UtcNow, "", "");

    /// <summary>Maps one initial-snapshot <see cref="ActiveAlarmSnapshot"/> entry to a Snapshot transition.</summary>
    /// <param name="snapshot">The active alarm snapshot proto message to map.</param>
    /// <returns>A <see cref="NativeAlarmTransition"/> with <c>AlarmTransitionKind.Snapshot</c>.</returns>
    public static NativeAlarmTransition MapSnapshot(ActiveAlarmSnapshot snapshot) => new(
        SourceReference: snapshot.AlarmFullReference,
        SourceObjectReference: snapshot.SourceObjectReference,
        AlarmTypeName: snapshot.AlarmTypeName,
        Kind: AlarmTransitionKind.Snapshot,
        Condition: MapConditionState(snapshot.CurrentState, snapshot.Severity),
        Category: snapshot.Category,
        Description: snapshot.Description,
        Message: snapshot.Description,
        OperatorUser: snapshot.OperatorUser,
        OperatorComment: snapshot.OperatorComment,
        OriginalRaiseTime: snapshot.OriginalRaiseTimestamp?.ToDateTimeOffset(),
        TransitionTime: snapshot.LastTransitionTimestamp?.ToDateTimeOffset() ?? DateTimeOffset.UtcNow,
        CurrentValue: "",
        LimitValue: "");
}
