using ScadaLink.Commons.Messages.InboundApi;
using ScadaLink.Communication;

namespace ScadaLink.CentralUI.ScriptAnalysis;

/// <summary>
/// Backs the Test Run sandbox <c>Instance</c> when the run is bound to a real
/// deployed instance. Routes attribute reads/writes and sibling-script calls to
/// the instance cross-site via <see cref="CommunicationService"/> — the same
/// transport the inbound API's <c>Route.To()</c> uses. All calls run under the
/// Test Run's cancellation token, so the sandbox timeout still applies.
/// </summary>
public sealed class SandboxInstanceGateway : ISandboxInstanceGateway
{
    private readonly CommunicationService _comms;
    private readonly string _siteId;
    private readonly string _instanceUniqueName;
    private readonly CancellationToken _runToken;

    public SandboxInstanceGateway(
        CommunicationService comms,
        string siteId,
        string instanceUniqueName,
        CancellationToken runToken)
    {
        _comms = comms;
        _siteId = siteId;
        _instanceUniqueName = instanceUniqueName;
        _runToken = runToken;
    }

    public async Task<object?> GetAttributeAsync(string canonicalName, CancellationToken ct)
    {
        var request = new RouteToGetAttributesRequest(
            Guid.NewGuid().ToString(), _instanceUniqueName,
            new[] { canonicalName }, DateTimeOffset.UtcNow);
        var response = await _comms.RouteToGetAttributesAsync(_siteId, request, _runToken);
        if (!response.Success)
            throw new ScriptSandboxException(
                $"GetAttribute(\"{canonicalName}\") on bound instance '{_instanceUniqueName}' failed: {response.ErrorMessage}");
        return response.Values.TryGetValue(canonicalName, out var value) ? value : null;
    }

    public async Task SetAttributeAsync(string canonicalName, string value, CancellationToken ct)
    {
        var request = new RouteToSetAttributesRequest(
            Guid.NewGuid().ToString(), _instanceUniqueName,
            new Dictionary<string, string> { [canonicalName] = value }, DateTimeOffset.UtcNow);
        var response = await _comms.RouteToSetAttributesAsync(_siteId, request, _runToken);
        if (!response.Success)
            throw new ScriptSandboxException(
                $"SetAttribute(\"{canonicalName}\") on bound instance '{_instanceUniqueName}' failed: {response.ErrorMessage}");
    }

    public async Task<object?> CallScriptAsync(
        string canonicalScriptName, IReadOnlyDictionary<string, object?>? parameters, CancellationToken ct)
    {
        var request = new RouteToCallRequest(
            Guid.NewGuid().ToString(), _instanceUniqueName,
            canonicalScriptName, parameters, DateTimeOffset.UtcNow);
        var response = await _comms.RouteToCallAsync(_siteId, request, _runToken);
        if (!response.Success)
            throw new ScriptSandboxException(
                $"CallScript(\"{canonicalScriptName}\") on bound instance '{_instanceUniqueName}' failed: {response.ErrorMessage}");
        return response.ReturnValue;
    }
}
