using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms;

namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;

/// <summary>
/// Configuration options for OPC UA connections, parsed from connection details JSON.
/// All values have defaults matching the OPC Foundation SDK's typical settings.
/// </summary>
public record OpcUaConnectionOptions(
    int SessionTimeoutMs = 60000,
    int OperationTimeoutMs = 15000,
    int PublishingIntervalMs = 1000,
    int KeepAliveCount = 10,
    int LifetimeCount = 30,
    int MaxNotificationsPerPublish = 100,
    int SamplingIntervalMs = 1000,
    int QueueSize = 10,
    string SecurityMode = "None",
    // DataConnectionLayer-012: secure-by-default — untrusted server certificates are
    // rejected unless an operator explicitly opts in per connection. Accepting any
    // certificate defeats the Sign / SignAndEncrypt modes against a man-in-the-middle.
    bool AutoAcceptUntrustedCerts = false,
    bool DiscardOldest = true,
    byte SubscriptionPriority = 0,
    string SubscriptionDisplayName = "ScadaBridge",
    string TimestampsToReturn = "Source",
    OpcUaDeadbandOptions? Deadband = null,
    OpcUaUserIdentityOptions? UserIdentity = null);

public record OpcUaDeadbandOptions(string Type, double Value);

public record OpcUaUserIdentityOptions(
    string TokenType,
    string Username,
    string Password,
    string CertificatePath,
    string CertificatePassword);

/// <summary>
/// WP-7: Abstraction over OPC UA client library for testability.
/// The real implementation would wrap an OPC UA SDK (e.g., OPC Foundation .NET Standard Library).
/// </summary>
public interface IOpcUaClient : IAsyncDisposable
{
    /// <summary>
    /// Connects to an OPC UA server at the specified endpoint URL.
    /// </summary>
    /// <param name="endpointUrl">The OPC UA server endpoint URL.</param>
    /// <param name="options">Connection options; if null, defaults are used.</param>
    /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
    /// <returns>A task representing the asynchronous operation.</returns>
    Task ConnectAsync(string endpointUrl, OpcUaConnectionOptions? options = null, CancellationToken cancellationToken = default);
    /// <summary>
    /// Disconnects from the OPC UA server.
    /// </summary>
    /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
    /// <returns>A task representing the asynchronous operation.</returns>
    Task DisconnectAsync(CancellationToken cancellationToken = default);
    /// <summary>
    /// Gets a value indicating whether the client is currently connected to the server.
    /// </summary>
    bool IsConnected { get; }

    /// <summary>
    /// Creates a monitored item subscription for a node. Returns a subscription handle.
    /// </summary>
    /// <param name="nodeId">The OPC UA node ID to monitor.</param>
    /// <param name="onValueChanged">Callback invoked when the monitored value changes, receiving nodeId, value, sourceTimestamp, and statusCode.</param>
    /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
    /// <returns>A task that completes with a subscription handle string.</returns>
    Task<string> CreateSubscriptionAsync(
        string nodeId,
        Action<string, object?, DateTime, uint> onValueChanged,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Removes a monitored item subscription by handle.
    /// </summary>
    /// <param name="subscriptionHandle">The subscription handle returned by CreateSubscriptionAsync.</param>
    /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
    /// <returns>A task representing the asynchronous operation.</returns>
    Task RemoveSubscriptionAsync(string subscriptionHandle, CancellationToken cancellationToken = default);

    /// <summary>
    /// Subscribes to OPC UA Alarms &amp; Conditions events under
    /// <paramref name="sourceNodeId"/> (or the Server object when null). On
    /// (re)subscribe the adapter issues a ConditionRefresh and replays the
    /// active conditions as Snapshot…SnapshotComplete transitions. Returns a
    /// handle for <see cref="RemoveAlarmSubscriptionAsync"/>.
    /// </summary>
    /// <param name="sourceNodeId">OPC UA node ID to monitor; null subscribes to the Server object.</param>
    /// <param name="conditionFilter">Optional OPC UA condition type filter; null subscribes to all conditions.</param>
    /// <param name="onTransition">Callback invoked for each alarm transition received from the server.</param>
    /// <param name="cancellationToken">Token to observe for cancellation.</param>
    /// <returns>A task that resolves to a subscription handle string for use with <see cref="RemoveAlarmSubscriptionAsync"/>.</returns>
    Task<string> CreateAlarmSubscriptionAsync(
        string? sourceNodeId,
        string? conditionFilter,
        Action<NativeAlarmTransition> onTransition,
        CancellationToken cancellationToken = default);

    /// <summary>Removes an alarm-event subscription by handle.</summary>
    /// <param name="subscriptionHandle">The handle returned by <see cref="CreateAlarmSubscriptionAsync"/>.</param>
    /// <param name="cancellationToken">Token to observe for cancellation.</param>
    /// <returns>A task representing the asynchronous operation.</returns>
    Task RemoveAlarmSubscriptionAsync(string subscriptionHandle, CancellationToken cancellationToken = default);

    /// <summary>
    /// Reads the current value of a node.
    /// </summary>
    /// <param name="nodeId">The OPC UA node ID to read.</param>
    /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
    /// <returns>A task that completes with a tuple of (value, sourceTimestamp, statusCode).</returns>
    Task<(object? Value, DateTime SourceTimestamp, uint StatusCode)> ReadValueAsync(
        string nodeId, CancellationToken cancellationToken = default);

    /// <summary>
    /// Writes a value to a node.
    /// </summary>
    /// <param name="nodeId">The OPC UA node ID to write to.</param>
    /// <param name="value">The value to write.</param>
    /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
    /// <returns>A task that completes with the OPC UA status code of the write operation.</returns>
    Task<uint> WriteValueAsync(string nodeId, object? value, CancellationToken cancellationToken = default);

    /// <summary>
    /// Raised when the OPC UA session detects a keep-alive failure or the server
    /// becomes unreachable. The adapter layer uses this to trigger reconnection.
    /// </summary>
    event Action? ConnectionLost;

    /// <summary>
    /// Enumerates the immediate children of <paramref name="parentNodeId"/>
    /// (or the server's ObjectsFolder when null). Throws
    /// <see cref="ConnectionNotConnectedException"/> when the session is not
    /// currently up.
    /// </summary>
    /// <param name="parentNodeId">Node id whose children to browse, or null for the server root.</param>
    /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
    /// <returns>A task that completes with the immediate children of the requested node.</returns>
    Task<BrowseChildrenResult> BrowseChildrenAsync(
        string? parentNodeId,
        CancellationToken cancellationToken = default);
}

/// <summary>
/// Factory for creating IOpcUaClient instances.
/// </summary>
public interface IOpcUaClientFactory
{
    /// <summary>
    /// Creates a new IOpcUaClient instance.
    /// </summary>
    /// <returns>A new IOpcUaClient instance.</returns>
    IOpcUaClient Create();
}

/// <summary>
/// Default factory that creates stub OPC UA clients.
/// In production, this would create real OPC UA SDK client instances.
/// </summary>
public class DefaultOpcUaClientFactory : IOpcUaClientFactory
{
    /// <inheritdoc />
    public IOpcUaClient Create() => new StubOpcUaClient();
}

/// <summary>
/// Stub OPC UA client for development/testing. A real implementation would
/// wrap the OPC Foundation .NET Standard Library.
/// </summary>
internal class StubOpcUaClient : IOpcUaClient
{
    /// <inheritdoc />
    public bool IsConnected { get; private set; }
#pragma warning disable CS0067
    /// <summary>Raised when the OPC UA session detects a keep-alive failure or the server becomes unreachable.</summary>
    public event Action? ConnectionLost;
#pragma warning restore CS0067

    /// <inheritdoc />
    public Task ConnectAsync(string endpointUrl, OpcUaConnectionOptions? options = null, CancellationToken cancellationToken = default)
    {
        IsConnected = true;
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public Task DisconnectAsync(CancellationToken cancellationToken = default)
    {
        IsConnected = false;
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public Task<string> CreateSubscriptionAsync(
        string nodeId, Action<string, object?, DateTime, uint> onValueChanged,
        CancellationToken cancellationToken = default)
    {
        return Task.FromResult(Guid.NewGuid().ToString());
    }

    /// <inheritdoc />
    public Task RemoveSubscriptionAsync(string subscriptionHandle, CancellationToken cancellationToken = default)
    {
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public Task<string> CreateAlarmSubscriptionAsync(
        string? sourceNodeId, string? conditionFilter,
        Action<NativeAlarmTransition> onTransition, CancellationToken cancellationToken = default)
    {
        // Stub: no events. Real A&C subscription lives in RealOpcUaClient.
        return Task.FromResult(Guid.NewGuid().ToString());
    }

    /// <inheritdoc />
    public Task RemoveAlarmSubscriptionAsync(string subscriptionHandle, CancellationToken cancellationToken = default)
        => Task.CompletedTask;

    /// <inheritdoc />
    public Task<(object? Value, DateTime SourceTimestamp, uint StatusCode)> ReadValueAsync(
        string nodeId, CancellationToken cancellationToken = default)
    {
        return Task.FromResult<(object?, DateTime, uint)>((null, DateTime.UtcNow, 0));
    }

    /// <inheritdoc />
    public Task<uint> WriteValueAsync(string nodeId, object? value, CancellationToken cancellationToken = default)
    {
        return Task.FromResult<uint>(0); // Good status
    }

    /// <inheritdoc />
    public Task<BrowseChildrenResult> BrowseChildrenAsync(
        string? parentNodeId, CancellationToken cancellationToken = default)
        => throw new NotImplementedException();

    /// <summary>Disposes this stub client and marks the connection as closed.</summary>
    /// <returns>A completed <see cref="ValueTask"/>.</returns>
    public ValueTask DisposeAsync()
    {
        IsConnected = false;
        return ValueTask.CompletedTask;
    }
}
