using System.Data.Common;
using ScadaLink.Commons.Interfaces.Services;
using ScadaLink.Commons.Messages.Notification;

namespace ScadaLink.CentralUI.ScriptAnalysis;

/// <summary>
/// User-facing surface for <c>ExternalSystem.Call</c> /
/// <c>ExternalSystem.CachedCall</c> inside a Test Run. Mirrors
/// ExternalSystemHelper in ScadaLink.SiteRuntime.Scripts.ScriptRuntimeContext
/// so the same user code compiles against both. When constructed with a null
/// client (the editor's metadata-only analysis pass) every call throws
/// <see cref="ScriptSandboxException"/>; with a real client wired in (a Test
/// Run) calls hit the live HTTP path.
/// </summary>
public class SandboxExternalHelper
{
    private readonly IExternalSystemClient? _client;
    private readonly string _instanceName;

    public SandboxExternalHelper(IExternalSystemClient? client, string instanceName)
    {
        _client = client;
        _instanceName = instanceName;
    }

    public Task<ExternalCallResult> Call(
        string systemName,
        string methodName,
        IReadOnlyDictionary<string, object?>? parameters = null,
        CancellationToken cancellationToken = default)
    {
        if (_client == null)
            throw new ScriptSandboxException(
                $"External.Call(\"{systemName}\", \"{methodName}\") — external system client not configured for Test Run.");
        return _client.CallAsync(systemName, methodName, parameters, cancellationToken);
    }

    public Task<ExternalCallResult> CachedCall(
        string systemName,
        string methodName,
        IReadOnlyDictionary<string, object?>? parameters = null,
        CancellationToken cancellationToken = default)
    {
        if (_client == null)
            throw new ScriptSandboxException(
                $"External.CachedCall(\"{systemName}\", \"{methodName}\") — external system client not configured for Test Run.");
        return _client.CachedCallAsync(systemName, methodName, parameters, _instanceName, cancellationToken);
    }
}

public class SandboxDatabaseHelper
{
    private readonly IDatabaseGateway? _gateway;
    private readonly string _instanceName;

    public SandboxDatabaseHelper(IDatabaseGateway? gateway, string instanceName)
    {
        _gateway = gateway;
        _instanceName = instanceName;
    }

    public Task<DbConnection> Connection(string name, CancellationToken cancellationToken = default)
    {
        if (_gateway == null)
            throw new ScriptSandboxException(
                $"Database.Connection(\"{name}\") — database gateway not configured for Test Run.");
        return _gateway.GetConnectionAsync(name, cancellationToken);
    }

    public Task CachedWrite(
        string name,
        string sql,
        IReadOnlyDictionary<string, object?>? parameters = null,
        CancellationToken cancellationToken = default)
    {
        if (_gateway == null)
            throw new ScriptSandboxException(
                $"Database.CachedWrite(\"{name}\") — database gateway not configured for Test Run.");
        return _gateway.CachedWriteAsync(name, sql, parameters, _instanceName, cancellationToken);
    }
}

/// <summary>
/// Sandbox mirror of <c>ScadaLink.SiteRuntime.Scripts.NotifyHelper</c> — the
/// <c>Notify</c> global. Signature-faithful to production so the same user code
/// (<c>Notify.To(...).Send(...)</c> / <c>Notify.Status(...)</c>) compiles
/// identically against both surfaces.
///
/// In the Notification Outbox design production no longer delivers notification
/// email inline — <c>Notify.Send</c> enqueues into the site Store-and-Forward
/// Engine and returns a <c>NotificationId</c>. The sandbox has no S&amp;F engine
/// and no central, so it is a pure no-op fake: <c>Send</c> returns a generated
/// fake id and <c>Status</c> returns a placeholder <see cref="NotificationDeliveryStatus"/>.
/// Nothing is delivered.
/// </summary>
public class SandboxNotifyHelper
{
    /// <summary>Selects the notification list to send to.</summary>
    public SandboxNotifyTarget To(string listName) =>
        new();

    /// <summary>
    /// Queries the delivery status of a previously-sent notification. The
    /// sandbox never delivers, so this always reports the placeholder
    /// <c>Unknown</c> status — it exists for signature fidelity with
    /// <c>NotifyHelper.Status</c>.
    /// </summary>
    public Task<NotificationDeliveryStatus> Status(string notificationId) =>
        Task.FromResult(new NotificationDeliveryStatus("Unknown", 0, null, null));
}

/// <summary>
/// Sandbox mirror of <c>ScadaLink.SiteRuntime.Scripts.NotifyTarget</c> — the
/// target of <c>Notify.To("listName")</c>.
/// </summary>
public class SandboxNotifyTarget
{
    internal SandboxNotifyTarget()
    {
    }

    /// <summary>
    /// Mirrors <c>NotifyTarget.Send</c> — returns a <c>NotificationId</c>. In
    /// the sandbox nothing is enqueued or delivered; a fake id is returned so
    /// the call type-checks identically to production.
    /// </summary>
    public Task<string> Send(string subject, string message, CancellationToken cancellationToken = default) =>
        Task.FromResult(Guid.NewGuid().ToString("N"));
}
