using Microsoft.EntityFrameworkCore;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
using ZB.MOM.WW.OtOpcUa.Configuration;

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;

/// <summary>
/// Secrets-adoption (G-2c) regression guard: the AdminUI driver-config editor persists <c>DriverConfig</c>
/// as an OPAQUE JSON string through <see cref="RawTreeService"/> (see <c>UpdateDriverAsync</c> /
/// <c>CreateDriverAsync</c> — <c>entity.DriverConfig = driverConfigJson</c> verbatim, and
/// <c>LoadDriverForEditAsync</c> reads it back verbatim). A <c>secret:</c>/<c>env:</c>/<c>file:</c>
/// reference stored in a driver secret field (OpcUaClient <c>Password</c>/<c>UserCertificatePassword</c>,
/// Galaxy <c>Gateway.ApiKeySecretRef</c>) MUST survive save→reload as the literal ref string — the save
/// path must never resolve-then-resave cleartext (which would re-leak the secret into the config store and
/// defeat G-2). Secret resolution belongs ONLY at driver-instantiation / Test-connect time (Tasks 7-8),
/// never on the AdminUI persistence path — and indeed <see cref="RawTreeService"/> takes no secret resolver
/// (its only dependency is the DbContext factory), so resolution is structurally impossible here.
/// <para>
/// Reuses the shared <see cref="RawTreeTestDb"/> InMemory fixture, matching the WP3 driver-config tests.
/// </para>
/// </summary>
[Trait("Category", "Unit")]
public sealed class RawTreeServiceSecretRefRoundTripTests
{
    private static (RawTreeService Service, string DbName) Seeded()
    {
        var name = RawTreeTestDb.SeedFresh();
        return (new RawTreeService(RawTreeTestDb.Factory(name)), name);
    }

    private static byte[] RowVersionOf(string dbName, string driverId)
    {
        using var db = RawTreeTestDb.CreateNamed(dbName);
        return db.DriverInstances.Single(d => d.DriverInstanceId == driverId).RowVersion;
    }

    [Fact]
    public async Task UpdateDriver_preserves_OpcUaClient_secret_refs_verbatim_on_save_and_reload()
    {
        var (service, dbName) = Seeded();
        var rv = RowVersionOf(dbName, RawTreeTestDb.FolderedDriverId);

        // The editor emits the raw ref strings the user typed — they must NOT be resolved on save.
        const string config =
            "{\"Password\":\"secret:opcua/x/pw\",\"UserCertificatePassword\":\"secret:opcua/x/certpw\"}";

        var result = await service.UpdateDriverAsync(
            RawTreeTestDb.FolderedDriverId, "modbus-1", config, null, rv);
        result.Ok.ShouldBeTrue();

        // Reload through the same seam the modal uses.
        var reloaded = await service.LoadDriverForEditAsync(RawTreeTestDb.FolderedDriverId);
        reloaded.ShouldNotBeNull();

        // Byte-identical: nothing on the save path rewrote the string.
        reloaded!.DriverConfig.ShouldBe(config);
        // The literal refs survived (not resolved to cleartext, not stripped).
        reloaded.DriverConfig.ShouldContain("secret:opcua/x/pw");
        reloaded.DriverConfig.ShouldContain("secret:opcua/x/certpw");

        // And the raw column agrees with the projection.
        using var db = RawTreeTestDb.CreateNamed(dbName);
        db.DriverInstances.Single(d => d.DriverInstanceId == RawTreeTestDb.FolderedDriverId)
            .DriverConfig.ShouldBe(config);
    }

    [Fact]
    public async Task CreateDriver_preserves_Galaxy_ApiKeySecretRef_verbatim_on_save_and_reload()
    {
        var (service, dbName) = Seeded();

        const string config = "{\"Gateway\":{\"ApiKeySecretRef\":\"secret:galaxy/x/apikey\"}}";

        var created = await service.CreateDriverAsync(
            RawTreeTestDb.ClusterId, RawTreeTestDb.RootFolderId, "galaxy-1", "Galaxy", config);
        created.Ok.ShouldBeTrue();
        created.CreatedId.ShouldNotBeNull();

        var reloaded = await service.LoadDriverForEditAsync(created.CreatedId!);
        reloaded.ShouldNotBeNull();

        reloaded!.DriverConfig.ShouldBe(config);
        reloaded.DriverConfig.ShouldContain("secret:galaxy/x/apikey");

        using var db = RawTreeTestDb.CreateNamed(dbName);
        db.DriverInstances.Single(d => d.DriverInstanceId == created.CreatedId)
            .DriverConfig.ShouldBe(config);
    }

    [Fact]
    public async Task UpdateDriver_preserves_env_and_file_ref_forms_verbatim()
    {
        var (service, dbName) = Seeded();
        var rv = RowVersionOf(dbName, RawTreeTestDb.FolderedDriverId);

        const string config =
            "{\"Password\":\"env:OPCUA_PW\",\"UserCertificatePassword\":\"file:/run/secrets/certpw\"}";

        var result = await service.UpdateDriverAsync(
            RawTreeTestDb.FolderedDriverId, "modbus-1", config, null, rv);
        result.Ok.ShouldBeTrue();

        var reloaded = await service.LoadDriverForEditAsync(RawTreeTestDb.FolderedDriverId);
        reloaded!.DriverConfig.ShouldBe(config);
        reloaded.DriverConfig.ShouldContain("env:OPCUA_PW");
        reloaded.DriverConfig.ShouldContain("file:/run/secrets/certpw");
    }
}
