using Microsoft.AspNetCore.Components.Authorization;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
using ZB.MOM.WW.ScadaBridge.Communication;
using ZB.MOM.WW.ScadaBridge.Security;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;

/// <summary>
/// Default <see cref="IBindingTester"/> implementation — a thin facade over
/// <see cref="CommunicationService.ReadTagValuesAsync"/> that enforces the
/// CentralUI-side <c>Design</c>-role trust boundary and translates transport
/// exceptions into a typed <see cref="ReadTagValuesFailure"/> result. Mirrors
/// <see cref="BrowseService"/>.
/// </summary>
public sealed class BindingTester : IBindingTester
{
    private readonly CommunicationService _communication;
    private readonly AuthenticationStateProvider _auth;

    /// <summary>
    /// Initializes a new instance of the <see cref="BindingTester"/>.
    /// </summary>
    /// <param name="communication">Central-side cluster communication service.</param>
    /// <param name="auth">Authentication state provider used for the Design-role guard.</param>
    public BindingTester(CommunicationService communication, AuthenticationStateProvider auth)
    {
        _communication = communication ?? throw new ArgumentNullException(nameof(communication));
        _auth = auth ?? throw new ArgumentNullException(nameof(auth));
    }

    /// <inheritdoc/>
    public async Task<ReadTagValuesResult> ReadAsync(
        string siteId,
        string connectionName,
        IReadOnlyList<string> tagPaths,
        CancellationToken ct = default)
    {
        // CentralUI-side role guard — sites don't enforce envelope-level
        // roles, so the Designer check must happen here before any cross-cluster
        // traffic. Use HasClaim against JwtTokenService.RoleClaimType (not
        // IsInRole, per c1e16cf).
        var state = await _auth.GetAuthenticationStateAsync();
        if (!state.User.HasClaim(JwtTokenService.RoleClaimType, Roles.Designer))
        {
            return new ReadTagValuesResult(
                Array.Empty<TagReadOutcome>(),
                new ReadTagValuesFailure(ReadTagValuesFailureKind.ServerError, "Not authorized."));
        }

        try
        {
            return await _communication.ReadTagValuesAsync(
                siteId,
                new ReadTagValuesCommand(connectionName, tagPaths),
                ct);
        }
        catch (TimeoutException ex)
        {
            // Akka Ask timed out — the site (or its OPC UA session) didn't
            // answer within CommunicationOptions.QueryTimeout. Surface as a
            // typed Timeout failure so the dialog can render an inline banner.
            return new ReadTagValuesResult(
                Array.Empty<TagReadOutcome>(),
                new ReadTagValuesFailure(ReadTagValuesFailureKind.Timeout, ex.Message));
        }
        catch (OperationCanceledException)
        {
            // Caller-initiated cancel — propagate so Blazor can drop the
            // response cleanly. Distinct from Timeout.
            throw;
        }
        catch (Exception ex)
        {
            // Any other transport / serialization failure: keep the dialog
            // alive with a typed banner.
            return new ReadTagValuesResult(
                Array.Empty<TagReadOutcome>(),
                new ReadTagValuesFailure(ReadTagValuesFailureKind.ServerError, ex.Message));
        }
    }
}
