using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Nexus.Api.Data;
using Nexus.Api.Models;
using Nexus.Api.Services;
using Xunit;

namespace Nexus.Api.Tests;

public sealed class OpenClawWriteGateTests
{
    [Fact]
    public async Task Adopted_primary_profile_allows_matching_write_boundary()
    {
        var connector = ConnectedConnector();
        await using var fixture = await GateFixture.CreateAsync(connector);

        var result = await fixture.Gate.EvaluateAsync("agents.create");

        Assert.True(result.Allowed);
    }

    [Fact]
    public async Task Endpoint_change_after_adoption_blocks_write()
    {
        var connector = ConnectedConnector();
        await using var fixture = await GateFixture.CreateAsync(connector);
        connector.ActiveEndpoint = "wss://other-openclaw.example.test:18789/";

        var result = await fixture.Gate.EvaluateAsync("agents.create");

        Assert.False(result.Allowed);
        Assert.Equal("endpoint_trust_mismatch", result.State);
    }

    [Fact]
    public async Task Device_change_after_adoption_blocks_write()
    {
        var connector = ConnectedConnector();
        await using var fixture = await GateFixture.CreateAsync(connector);
        connector.DeviceId = "unexpected-device";

        var result = await fixture.Gate.EvaluateAsync("agents.create");

        Assert.False(result.Allowed);
        Assert.Equal("device_trust_mismatch", result.State);
    }

    [Fact]
    public async Task Wss_profile_without_tls_fingerprint_blocks_write()
    {
        var connector = ConnectedConnector();
        connector.ActiveTlsFingerprint = null;
        await using var fixture = await GateFixture.CreateAsync(connector);

        var result = await fixture.Gate.EvaluateAsync("agents.create");

        Assert.False(result.Allowed);
        Assert.Equal("tls_trust_missing", result.State);
    }

    [Fact]
    public async Task Internal_ws_profile_without_tls_fingerprint_can_write()
    {
        var connector = ConnectedConnector();
        connector.ActiveEndpoint = "ws://openclaw-gateway:18789/";
        connector.ActiveTlsFingerprint = null;
        await using var fixture = await GateFixture.CreateAsync(connector);

        var result = await fixture.Gate.EvaluateAsync("agents.create");

        Assert.True(result.Allowed);
    }

    private static StubOpenClawConnector ConnectedConnector()
        => new()
        {
            ConnectionState = GatewayConnectionState.Connected,
            GatewayVersion = "2026.8.0",
            RequiredVersion = "2026.8.0",
            ProtocolVersion = 4,
            ActiveEndpoint = "wss://openclaw.example.test:18789/",
            ActiveTlsFingerprint = new string('A', 64),
            DeviceId = "nexus-device",
            GrantedScopes = new HashSet<string>(
                ["operator.read", "operator.admin"],
                StringComparer.Ordinal),
            AdvertisedMethods = new HashSet<string>(
                ["agents.create"],
                StringComparer.Ordinal),
            AdvertisedEvents = new HashSet<string>(
                ["agent.updated"],
                StringComparer.Ordinal)
        };

    private sealed class GateFixture(
        ServiceProvider provider,
        OpenClawWriteGate gate) : IAsyncDisposable
    {
        public OpenClawWriteGate Gate { get; } = gate;

        public static async Task<GateFixture> CreateAsync(
            StubOpenClawConnector connector)
        {
            var services = new ServiceCollection();
            var databaseName = $"write-gate-{Guid.NewGuid():N}";
            services.AddDbContext<NexusDbContext>(options =>
                options.UseInMemoryDatabase(databaseName));
            var provider = services.BuildServiceProvider();
            var configuration = new ConfigurationBuilder()
                .AddInMemoryCollection(new Dictionary<string, string?>
                {
                    ["OpenClawSetup:ExternalClientIdentitySupported"] = "true"
                })
                .Build();
            var options = Options.Create(new GatewayConnectorOptions
            {
                ClientId = "nexus",
                ClientMode = "backend",
                ExternalClientIdentitySupported = true,
                AllowReservedInternalClientIdentity = false
            });

            await using (var scope = provider.CreateAsyncScope())
            {
                var db = scope.ServiceProvider
                    .GetRequiredService<NexusDbContext>();
                db.OpenClawConnectionProfiles.Add(
                    new OpenClawConnectionProfile
                    {
                        Endpoint = connector.ActiveEndpoint!,
                        DiscoverySource = "test",
                        RequiredVersion = connector.GatewayVersion,
                        TlsCertificateFingerprint =
                            connector.ActiveTlsFingerprint,
                        AdoptionState = OpenClawAdoptionStates.Adopted,
                        ManagementEnabled = true,
                        CapabilityHash =
                            OpenClawWriteGate.BuildCapabilityHash(connector),
                        DeviceId = connector.DeviceId,
                        Revision = 1
                    });
                await db.SaveChangesAsync();
            }

            return new GateFixture(
                provider,
                new OpenClawWriteGate(
                    provider.GetRequiredService<IServiceScopeFactory>(),
                    connector,
                    options,
                    configuration));
        }

        public ValueTask DisposeAsync() => provider.DisposeAsync();
    }
}
