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

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);
    }
}

public class SandboxNotifyHelper
{
    private readonly INotificationDeliveryService? _service;
    private readonly string _instanceName;

    public SandboxNotifyHelper(INotificationDeliveryService? service, string instanceName)
    {
        _service = service;
        _instanceName = instanceName;
    }

    public SandboxNotifyTarget To(string listName) =>
        new(listName, _service, _instanceName);
}

public class SandboxNotifyTarget
{
    private readonly string _listName;
    private readonly INotificationDeliveryService? _service;
    private readonly string _instanceName;

    internal SandboxNotifyTarget(string listName, INotificationDeliveryService? service, string instanceName)
    {
        _listName = listName;
        _service = service;
        _instanceName = instanceName;
    }

    public Task<NotificationResult> Send(string subject, string message, CancellationToken cancellationToken = default)
    {
        if (_service == null)
            throw new ScriptSandboxException(
                $"Notify.To(\"{_listName}\").Send(...) — notification service not configured for Test Run.");
        return _service.SendAsync(_listName, subject, message, _instanceName, cancellationToken);
    }
}
