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

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

/// <summary>
///     One live tag value as shown in the Browse subscription panel. Projected
///     from a worker <see cref="BulkReadResult"/> so the Razor component never
///     touches protobuf types directly.
/// </summary>
public sealed record DashboardTagValue(
    string TagAddress,
    bool Ok,
    string ValueText,
    string DataType,
    int Quality,
    bool QualityGood,
    DateTimeOffset? SourceTimestamp,
    string? Error)
{
    /// <summary>
    ///     Placeholder rendered in place of <see cref="ValueText"/> when
    ///     <c>MxGateway:Dashboard:ShowTagValues</c> is false (the default). The
    ///     substitution happens once, in <c>DashboardLiveDataService</c>, so the
    ///     Browse page renders whatever it is handed and no view has to repeat
    ///     the decision. Deliberately a visible marker rather than an empty
    ///     string: an operator must be able to tell a suppressed value from a
    ///     tag that read back blank.
    /// </summary>
    public const string RedactedValueText = "[redacted]";

    /// <summary>
    ///     Classic OPC-DA "Good" quality. MXAccess surfaces 192 for a healthy
    ///     advised value; anything lower is uncertain or bad.
    /// </summary>
    private const int GoodQualityThreshold = 192;

    /// <summary>Projects a worker bulk-read result into a dashboard tag value.</summary>
    /// <param name="result">The per-tag result from a <c>ReadBulk</c> reply.</param>
    /// <returns>The projected dashboard value.</returns>
    public static DashboardTagValue FromBulkReadResult(BulkReadResult result)
    {
        ArgumentNullException.ThrowIfNull(result);

        string? error = null;
        if (!result.WasSuccessful)
        {
            error = !string.IsNullOrWhiteSpace(result.ErrorMessage)
                ? result.ErrorMessage
                : FirstStatusDiagnostic(result);
        }

        return new DashboardTagValue(
            TagAddress: result.TagAddress,
            Ok: result.WasSuccessful,
            ValueText: DashboardMxValueFormatter.FormatValue(result.Value),
            DataType: DashboardMxValueFormatter.FormatDataType(result.Value),
            Quality: result.Quality,
            QualityGood: result.WasSuccessful && result.Quality >= GoodQualityThreshold,
            SourceTimestamp: result.SourceTimestamp?.ToDateTimeOffset(),
            Error: error);
    }

    private static string? FirstStatusDiagnostic(BulkReadResult result)
    {
        foreach (MxStatusProxy status in result.Statuses)
        {
            if (!string.IsNullOrWhiteSpace(status.DiagnosticText))
            {
                return status.DiagnosticText;
            }

            if (status.Category != MxStatusCategory.Ok)
            {
                return status.Category.ToString();
            }
        }

        return null;
    }
}
