namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;

/// <summary>
///     Driver capability for historical-data reads (OPC UA HistoryRead). Optional —
///     only drivers whose backends carry historian data implement this. Currently:
///     Galaxy (Wonderware Historian via the optional plugin), OPC UA Client (forward
///     to upstream server).
/// </summary>
/// <remarks>
///     <see cref="ReadAtTimeAsync"/> and <see cref="ReadEventsAsync"/> are C# default interface
///     methods that throw <see cref="NotSupportedException"/> — drivers opt in by overriding so
///     a raw-only driver compiles without forcing it to provide at-time / event surfaces it
///     has no backend for. The sibling server-side surface, <see cref="IHistorianDataSource"/>,
///     declares both methods as required because a registered historian owns the full read
///     surface; the asymmetry is intentional.
/// </remarks>
public interface IHistoryProvider
{
    /// <summary>
    ///     Read raw historical samples for a single attribute over a time range.
    ///     The Core wraps this with continuation-point handling.
    /// </summary>
    /// <param name="fullReference">The full reference path to the attribute.</param>
    /// <param name="startUtc">Inclusive lower bound on the time range (UTC).</param>
    /// <param name="endUtc">Exclusive upper bound on the time range (UTC).</param>
    /// <param name="maxValuesPerNode">Maximum number of values to return per node.</param>
    /// <param name="cancellationToken">A cancellation token to stop the operation.</param>
    /// <returns>A task that returns the history read result containing samples and optional continuation point.</returns>
    Task<HistoryReadResult> ReadRawAsync(
        string fullReference,
        DateTime startUtc,
        DateTime endUtc,
        uint maxValuesPerNode,
        CancellationToken cancellationToken);

    /// <summary>
    ///     Read processed (aggregated) samples — interval-bucketed average / min / max / etc.
    ///     Optional — drivers that only support raw history can throw <see cref="NotSupportedException"/>.
    /// </summary>
    /// <param name="fullReference">The full reference path to the attribute.</param>
    /// <param name="startUtc">Inclusive lower bound on the time range (UTC).</param>
    /// <param name="endUtc">Exclusive upper bound on the time range (UTC).</param>
    /// <param name="interval">The time interval for bucketing samples.</param>
    /// <param name="aggregate">The aggregate function to apply to each bucket.</param>
    /// <param name="cancellationToken">A cancellation token to stop the operation.</param>
    /// <returns>A task that returns the history read result containing aggregated samples and optional continuation point.</returns>
    Task<HistoryReadResult> ReadProcessedAsync(
        string fullReference,
        DateTime startUtc,
        DateTime endUtc,
        TimeSpan interval,
        HistoryAggregateType aggregate,
        CancellationToken cancellationToken);

    /// <summary>
    ///     Read one sample per requested timestamp — OPC UA HistoryReadAtTime service. The
    ///     driver interpolates (or returns the prior-boundary sample) when no exact match
    ///     exists. Optional; drivers that can't interpolate throw <see cref="NotSupportedException"/>.
    /// </summary>
    /// <remarks>
    ///     Default implementation throws. Drivers opt in by overriding; keeps existing
    ///     <c>IHistoryProvider</c> implementations compiling without forcing a ReadAtTime path
    ///     they may not have a backend for.
    /// </remarks>
    /// <param name="fullReference">The full reference path to the attribute.</param>
    /// <param name="timestampsUtc">The list of timestamps at which to read values.</param>
    /// <param name="cancellationToken">A cancellation token to stop the operation.</param>
    /// <returns>A task that returns the history read result containing samples at the requested times.</returns>
    Task<HistoryReadResult> ReadAtTimeAsync(
        string fullReference,
        IReadOnlyList<DateTime> timestampsUtc,
        CancellationToken cancellationToken)
        => throw new NotSupportedException(
            $"{GetType().Name} does not implement ReadAtTimeAsync. " +
            "Drivers whose backends support at-time reads override this method.");

    /// <summary>
    ///     Read historical alarm/event records — OPC UA HistoryReadEvents service. Distinct
    ///     from the live event stream — historical rows come from an event historian (Galaxy's
    ///     Alarm Provider history log, etc.) rather than the driver's active subscription.
    /// </summary>
    /// <param name="sourceName">
    ///     Optional filter: null means "all sources", otherwise restrict to events from that
    ///     source-object name. Drivers may ignore the filter if the backend doesn't support it.
    /// </param>
    /// <param name="startUtc">Inclusive lower bound on <c>EventTimeUtc</c>.</param>
    /// <param name="endUtc">Exclusive upper bound on <c>EventTimeUtc</c>.</param>
    /// <param name="maxEvents">
    ///     Upper cap on returned events — the driver's backend enforces this. The type is
    ///     <see cref="int"/> rather than <see cref="uint"/> (which the sibling raw / processed
    ///     reads use for <c>maxValuesPerNode</c>) because callers and downstream historian
    ///     adapters historically treat <c>maxEvents &lt;= 0</c> as a sentinel meaning
    ///     "use the backend's default cap" (see <c>WonderwareHistorianClient</c> /
    ///     <c>HistorianDataSource</c>). The asymmetry is intentional.
    ///
    ///     <b>Continuation contract when using the sentinel:</b> When <c>maxEvents &lt;= 0</c>
    ///     the backend applies its own cap. If the backend's cap truncates the result
    ///     (i.e. more matching events exist beyond the returned set), implementations MUST set
    ///     <see cref="HistoricalEventsResult.ContinuationPoint"/> to a non-null token so
    ///     callers can detect truncation and page. A null <c>ContinuationPoint</c> means all
    ///     matching events were returned — callers rely on this to decide whether to page.
    /// </param>
    /// <param name="cancellationToken">Request cancellation.</param>
    /// <returns>A task that returns the historical events result containing matching event records and optional continuation point.</returns>
    /// <remarks>
    ///     Default implementation throws. Drivers whose backend can serve historical events
    ///     override: Galaxy (Wonderware Alarm &amp; Events log) and the OPC UA Client driver
    ///     (forwarding HistoryReadEvents to its upstream server). Raw-only drivers such as Modbus
    ///     stay with the default and let callers see <c>BadHistoryOperationUnsupported</c>.
    ///
    ///     Note the type asymmetry with <see cref="ReadRawAsync"/> /
    ///     <see cref="ReadProcessedAsync"/> (both use <c>uint maxValuesPerNode</c>): event
    ///     readers accept a signed <c>int maxEvents</c> so callers can pass 0 / negative as a
    ///     "use default cap" sentinel without an extra parameter or overload.
    /// </remarks>
    Task<HistoricalEventsResult> ReadEventsAsync(
        string? sourceName,
        DateTime startUtc,
        DateTime endUtc,
        int maxEvents,
        CancellationToken cancellationToken)
        => throw new NotSupportedException(
            $"{GetType().Name} does not implement ReadEventsAsync. " +
            "Drivers whose backends have an event historian override this method.");
}

/// <summary>Result of a HistoryRead call.</summary>
/// <param name="Samples">Returned samples in chronological order.</param>
/// <param name="ContinuationPoint">Opaque token for the next call when more samples are available; null when complete.</param>
public sealed record HistoryReadResult(
    IReadOnlyList<DataValueSnapshot> Samples,
    byte[]? ContinuationPoint);

/// <summary>Aggregate function for processed history reads. Mirrors OPC UA Part 13 standard aggregates.</summary>
public enum HistoryAggregateType
{
    Average,
    Minimum,
    Maximum,
    Total,
    Count,
}

/// <summary>
///     One row returned by <see cref="IHistoryProvider.ReadEventsAsync"/> — a historical
///     alarm/event record, not the OPC UA live-event stream. Fields match the minimum set the
///     Server needs to populate a <c>HistoryEventFieldList</c> for HistoryReadEvents responses.
/// </summary>
/// <param name="EventId">Stable unique id for the event — driver-specific format.</param>
/// <param name="SourceName">Source object that emitted the event. May differ from the <c>sourceName</c> filter the caller passed (fuzzy matches).</param>
/// <param name="EventTimeUtc">Process-side timestamp — when the event actually occurred.</param>
/// <param name="ReceivedTimeUtc">Historian-side timestamp — when the historian persisted the row; may lag <paramref name="EventTimeUtc"/> by the historian's buffer flush cadence.</param>
/// <param name="Message">Human-readable message text.</param>
/// <param name="Severity">OPC UA severity (1-1000). Drivers map their native priority scale onto this range.</param>
public sealed record HistoricalEvent(
    string EventId,
    string? SourceName,
    DateTime EventTimeUtc,
    DateTime ReceivedTimeUtc,
    string? Message,
    ushort Severity);

/// <summary>Result of a <see cref="IHistoryProvider.ReadEventsAsync"/> call.</summary>
/// <param name="Events">Events in chronological order by <c>EventTimeUtc</c>.</param>
/// <param name="ContinuationPoint">Opaque token for the next call when more events are available; null when complete.</param>
public sealed record HistoricalEventsResult(
    IReadOnlyList<HistoricalEvent> Events,
    byte[]? ContinuationPoint);
