namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Instance;

/// <summary>
/// Request to wait, event-driven, until an attribute reaches a value (or any
/// value satisfying a predicate), bounded by a timeout — the backing protocol for
/// the script-facing <c>Attributes.WaitAsync</c> helper.
///
/// <para>
/// <b>Site-local only.</b> The optional <see cref="Predicate"/> is a non-serializable
/// in-process delegate, so this message MUST flow only within a single site node's
/// actor system (script execution → Instance Actor). It is never sent across the
/// site↔central gRPC boundary. The value-equality form (<see cref="TargetValueEncoded"/>)
/// would serialize, but the routed/inbound variant is deliberately out of scope here.
/// </para>
/// </summary>
/// <param name="CorrelationId">Per-wait correlation id; keys the waiter registry and the timeout self-message.</param>
/// <param name="InstanceName">The instance this wait targets.</param>
/// <param name="AttributeName">The attribute to watch — already scope-resolved by the accessor.</param>
/// <param name="TargetValueEncoded">
/// The codec-encoded target value (<c>AttributeValueCodec.Encode(target)</c>). A
/// match compares the codec-encoded form of the current value against this string.
/// When both this and <see cref="Predicate"/> are null the wait matches on ANY change.
/// </param>
/// <param name="Predicate">
/// Site-local predicate tested against the raw (decoded) current value. Mutually
/// exclusive with <see cref="TargetValueEncoded"/> — null when the encoded target is used.
/// </param>
/// <param name="Timeout">How long to wait before self-evicting with a timeout reply.</param>
/// <param name="OccurredAtUtc">When the request was issued (UTC).</param>
/// <param name="RequireGoodQuality">
/// Quality-gated ("Good"-only) mode (spec §4.2): when <see langword="true"/>, a
/// match additionally requires the attribute quality to be exactly
/// <c>"Good"</c> (<see cref="System.StringComparison.Ordinal"/>) — a value that
/// reaches the target / satisfies the predicate at Bad/Uncertain quality is NOT a
/// match and the waiter stays pending until the value satisfies the test at Good
/// quality (or times out). Defaults to <see langword="false"/> (quality-agnostic:
/// the match tests the value only). Trailing/defaulted so existing positional
/// constructions compile unchanged.
/// </param>
public record WaitForAttributeRequest(
    string CorrelationId,
    string InstanceName,
    string AttributeName,
    string? TargetValueEncoded,
    Func<object?, bool>? Predicate,
    TimeSpan Timeout,
    DateTimeOffset OccurredAtUtc,
    bool RequireGoodQuality = false);

/// <summary>
/// Reply to a <see cref="WaitForAttributeRequest"/>. Exactly one of
/// <see cref="Matched"/> / <see cref="TimedOut"/> is set on the happy paths;
/// <see cref="ErrorMessage"/> is populated on the failure paths (per-instance
/// waiter cap exceeded, or the match predicate threw).
/// </summary>
/// <param name="CorrelationId">Echoes the request's correlation id.</param>
/// <param name="Matched">True when the attribute reached the target/predicate within the timeout.</param>
/// <param name="Value">The matched value (null on timeout / error).</param>
/// <param name="Quality">
/// The attribute quality at match time; <see langword="null"/> on the non-match
/// paths (timeout / error / cap-exceeded), matching the nullable
/// <see cref="ErrorMessage"/> convention.
/// </param>
/// <param name="TimedOut">True when the timeout fired before a match.</param>
/// <param name="ErrorMessage">
/// Non-null only when the wait failed/refused — the per-instance waiter cap was
/// exceeded, or the match predicate threw (<c>"Wait predicate threw: …"</c>).
/// </param>
public record WaitForAttributeResponse(
    string CorrelationId,
    bool Matched,
    object? Value,
    string? Quality,
    bool TimedOut,
    string? ErrorMessage = null);

/// <summary>
/// Internal self-message scheduled by the Instance Actor to fire a waiter's
/// timeout. Site-local only; never crosses a cluster boundary.
/// </summary>
/// <param name="CorrelationId">The waiter whose timeout fired.</param>
public record WaitForAttributeTimeout(string CorrelationId);
