using ZB.MOM.WW.MxGateway.Contracts.Proto;
using ZB.MOM.WW.MxGateway.Server.Alarms;

namespace ZB.MOM.WW.MxGateway.Server.Dashboard;

/// <summary>
///     Dashboard projection of an <see cref="AlarmProviderStatus" /> message
///     carried on the alarm feed. Maps the protobuf provider mode / degraded
///     flag into Bootstrap-only display fields so the Alarms page can render a
///     status badge without touching protobuf types.
/// </summary>
public sealed record DashboardAlarmProviderStatus(
    AlarmProviderMode Mode,
    bool IsDegraded,
    string Label,
    string BadgeCssClass,
    string Reason,
    DateTimeOffset? SinceUtc)
{
    /// <summary>Badge label shown when the alarm-manager provider is healthy.</summary>
    public const string AlarmManagerLabel = "Alarm Manager";

    /// <summary>Badge label shown when the feed has fallen back to subtag monitoring.</summary>
    public const string DegradedLabel = "Subtag monitoring (degraded)";

    /// <summary>
    ///     Badge label shown when the feed is in subtag monitoring because it was
    ///     deliberately configured (<c>Fallback:Mode=ForceSubtag</c>), as opposed
    ///     to an unexpected failover. A stable, intended state rather than a fault.
    /// </summary>
    public const string ForcedSubtagLabel = "Subtag monitoring (forced)";

    private const string HealthyBadge = "bg-success";
    private const string DegradedBadge = "bg-warning text-dark";

    // Cyan/info badge: visually distinct from the amber failover-degraded badge —
    // forced subtag is an intentional configuration, not an alarm-manager fault.
    private const string ForcedSubtagBadge = "bg-info text-dark";

    /// <summary>
    ///     The default status assumed before the first provider-status message
    ///     arrives: healthy alarm-manager mode.
    /// </summary>
    public static DashboardAlarmProviderStatus Healthy { get; } = new(
        Mode: AlarmProviderMode.Alarmmgr,
        IsDegraded: false,
        Label: AlarmManagerLabel,
        BadgeCssClass: HealthyBadge,
        Reason: string.Empty,
        SinceUtc: null);

    /// <summary>Projects an alarm-feed provider-status payload into a dashboard badge model.</summary>
    /// <param name="status">The provider-status payload from an <see cref="AlarmFeedMessage" />.</param>
    /// <returns>The projected dashboard status.</returns>
    public static DashboardAlarmProviderStatus FromProviderStatus(AlarmProviderStatus status)
    {
        ArgumentNullException.ThrowIfNull(status);

        // Treat the explicit degraded flag and the SUBTAG mode as equivalent;
        // the contract sets degraded=true whenever mode == SUBTAG, but guard
        // against either being set independently.
        bool degraded = status.Degraded || status.Mode == AlarmProviderMode.Subtag;
        string reason = status.Reason ?? string.Empty;

        // A configured ForceSubtag start carries the well-known forced reason and
        // is a deliberate mode, not a failover — render it distinctly so an
        // operator isn't alarmed by a "(degraded)" badge for an intended config.
        bool forced = degraded
            && status.Mode == AlarmProviderMode.Subtag
            && string.Equals(reason, AlarmProviderReasons.ForcedSubtag, StringComparison.Ordinal);

        string label = !degraded ? AlarmManagerLabel : forced ? ForcedSubtagLabel : DegradedLabel;
        string badge = !degraded ? HealthyBadge : forced ? ForcedSubtagBadge : DegradedBadge;

        return new DashboardAlarmProviderStatus(
            Mode: status.Mode,
            IsDegraded: degraded,
            Label: label,
            BadgeCssClass: badge,
            Reason: reason,
            SinceUtc: status.Since?.ToDateTimeOffset());
    }

    /// <summary>Projects an alarm-feed message into a dashboard badge model.</summary>
    /// <param name="message">An alarm-feed message whose payload is a provider status.</param>
    /// <returns>The projected dashboard status.</returns>
    /// <exception cref="ArgumentException">The message does not carry a provider-status payload.</exception>
    public static DashboardAlarmProviderStatus FromFeed(AlarmFeedMessage message)
    {
        ArgumentNullException.ThrowIfNull(message);

        if (message.PayloadCase != AlarmFeedMessage.PayloadOneofCase.ProviderStatus)
        {
            throw new ArgumentException(
                "Alarm-feed message does not carry a provider-status payload.",
                nameof(message));
        }

        return FromProviderStatus(message.ProviderStatus);
    }
}
