namespace ScadaLink.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 = "ScadaLink",
    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
{
    Task ConnectAsync(string endpointUrl, OpcUaConnectionOptions? options = null, CancellationToken cancellationToken = default);
    Task DisconnectAsync(CancellationToken cancellationToken = default);
    bool IsConnected { get; }

    /// <summary>
    /// Creates a monitored item subscription for a node. Returns a subscription handle.
    /// </summary>
    Task<string> CreateSubscriptionAsync(
        string nodeId,
        Action<string, object?, DateTime, uint> onValueChanged,
        CancellationToken cancellationToken = default);

    Task RemoveSubscriptionAsync(string subscriptionHandle, CancellationToken cancellationToken = default);

    Task<(object? Value, DateTime SourceTimestamp, uint StatusCode)> ReadValueAsync(
        string nodeId, CancellationToken cancellationToken = default);

    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>
/// Factory for creating IOpcUaClient instances.
/// </summary>
public interface IOpcUaClientFactory
{
    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
{
    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
{
    public bool IsConnected { get; private set; }
#pragma warning disable CS0067
    public event Action? ConnectionLost;
#pragma warning restore CS0067

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

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

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

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

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

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

    public ValueTask DisposeAsync()
    {
        IsConnected = false;
        return ValueTask.CompletedTask;
    }
}
