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

/// <summary>
///     Server-side historian tag provisioning — ensures the historian knows about the tags the
///     address space historizes before values are written. Registered alongside
///     <see cref="IHistorianDataSource"/> and invoked by the address-space applier when historized
///     nodes are (re)applied.
/// </summary>
/// <remarks>
///     Provisioning is best-effort and <b>non-blocking</b>: an unreachable or erroring historian
///     never fails an address-space apply. Implementations return a
///     <see cref="HistorianProvisionResult"/> tally instead of throwing, so the applier can surface
///     a count without taking the server down. Non-historizable types are skipped (counted in
///     <see cref="HistorianProvisionResult.Skipped"/>), not failed.
/// </remarks>
public interface IHistorianProvisioning
{
    /// <summary>
    ///     Ensures the supplied historian tags exist (create-or-update). Never throws; a transport or
    ///     backend failure is reported via <see cref="HistorianProvisionResult.Failed"/>.
    /// </summary>
    /// <param name="requests">The tags to ensure, with their driver data type and optional metadata.</param>
    /// <param name="ct">A cancellation token for the operation.</param>
    /// <returns>A tally of how the requests were handled.</returns>
    Task<HistorianProvisionResult> EnsureTagsAsync(
        IReadOnlyList<HistorianTagProvisionRequest> requests, CancellationToken ct);
}

/// <summary>
///     A single historian tag to ensure — the driver-agnostic shape the applier hands to
///     <see cref="IHistorianProvisioning.EnsureTagsAsync"/>. A backend maps
///     <see cref="DataType"/> onto its native tag type and skips types it cannot historize.
/// </summary>
/// <param name="TagName">The full reference / tag name to ensure in the historian.</param>
/// <param name="DataType">The driver-agnostic data type, used to select the historian tag type.</param>
/// <param name="EngineeringUnit">Optional engineering unit (e.g. <c>degC</c>); <c>null</c> when unknown.</param>
/// <param name="Description">Optional human-readable description; <c>null</c> when unknown.</param>
public sealed record HistorianTagProvisionRequest(
    string TagName,
    DriverDataType DataType,
    string? EngineeringUnit,
    string? Description);

/// <summary>
///     The tally returned by <see cref="IHistorianProvisioning.EnsureTagsAsync"/>. The buckets
///     partition the input: <c>Requested == Ensured + Skipped + Failed</c>.
/// </summary>
/// <param name="Requested">Total tags submitted.</param>
/// <param name="Ensured">Tags the historian acknowledged as created or already present.</param>
/// <param name="Skipped">Tags whose data type is not historizable on the backend (never sent).</param>
/// <param name="Failed">Tags that were sent but the backend did not acknowledge (incl. a swallowed transport error).</param>
public sealed record HistorianProvisionResult(
    int Requested,
    int Ensured,
    int Skipped,
    int Failed);

/// <summary>
///     No-op <see cref="IHistorianProvisioning"/> — the applier's safe default when no historian
///     backend is registered. Every call returns an all-zero tally and never touches a backend.
/// </summary>
public sealed class NullHistorianProvisioning : IHistorianProvisioning
{
    /// <summary>The shared singleton instance.</summary>
    public static readonly NullHistorianProvisioning Instance = new();

    private NullHistorianProvisioning() { }

    /// <inheritdoc />
    public Task<HistorianProvisionResult> EnsureTagsAsync(
        IReadOnlyList<HistorianTagProvisionRequest> requests, CancellationToken ct) =>
        Task.FromResult(new HistorianProvisionResult(0, 0, 0, 0));
}
