using Microsoft.EntityFrameworkCore;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Schemas;
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase;
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Repositories;
using Xunit;

namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests;

/// <summary>
/// Round-trip tests for <see cref="SharedSchemaRepository"/> (M9 template-level JSON-Schema
/// library, Task T32a). Uses the in-memory SQLite harness (<see cref="SqliteTestHelper"/>)
/// — the schema is materialized via <c>EnsureCreated()</c> from the same model the
/// migration was generated from, so the tests run unconditionally (no live MSSQL needed)
/// while still exercising the real unique-<c>Name</c> index. Mirrors the
/// <c>SecurityRepositoryTests</c> in-memory harness shape.
/// </summary>
public class SharedSchemaRepositoryTests : IDisposable
{
    private readonly ScadaBridgeDbContext _context;
    private readonly SharedSchemaRepository _repository;

    public SharedSchemaRepositoryTests()
    {
        _context = SqliteTestHelper.CreateInMemoryContext();
        _repository = new SharedSchemaRepository(_context);
    }

    public void Dispose()
    {
        _context.Database.CloseConnection();
        _context.Dispose();
    }

    [Fact]
    public async Task Add_ThenGetByName_RoundTrips()
    {
        var schema = new SharedSchema
        {
            Name = "TankReading",
            Scope = "Plant",
            SchemaJson = "{\"type\":\"object\",\"properties\":{\"level\":{\"type\":\"number\"}}}",
        };

        var id = await _repository.AddAsync(schema);
        Assert.True(id > 0, "AddAsync should return the store-generated identity.");

        var byName = await _repository.GetByNameAsync("TankReading");
        Assert.NotNull(byName);
        Assert.Equal(id, byName!.Id);
        Assert.Equal("TankReading", byName.Name);
        Assert.Equal("Plant", byName.Scope);
        Assert.Equal(
            "{\"type\":\"object\",\"properties\":{\"level\":{\"type\":\"number\"}}}",
            byName.SchemaJson);

        var byId = await _repository.GetByIdAsync(id);
        Assert.NotNull(byId);
        Assert.Equal("TankReading", byId!.Name);
    }

    [Fact]
    public async Task GetByName_Unknown_ReturnsNull()
    {
        var missing = await _repository.GetByNameAsync("DoesNotExist");
        Assert.Null(missing);
    }

    [Fact]
    public async Task Add_DuplicateName_Throws()
    {
        await _repository.AddAsync(new SharedSchema { Name = "Dup", SchemaJson = "{}" });

        // The UNIQUE index on Name must reject the second insert.
        await Assert.ThrowsAnyAsync<DbUpdateException>(() =>
            _repository.AddAsync(new SharedSchema { Name = "Dup", SchemaJson = "{}" }));
    }

    [Fact]
    public async Task NullableScope_RoundTrips()
    {
        var id = await _repository.AddAsync(new SharedSchema { Name = "NoScope", SchemaJson = "{}" });

        var loaded = await _repository.GetByIdAsync(id);
        Assert.NotNull(loaded);
        Assert.Null(loaded!.Scope);
    }

    [Fact]
    public async Task List_ReturnsAll_OrderedByName()
    {
        await _repository.AddAsync(new SharedSchema { Name = "Bravo", SchemaJson = "{}" });
        await _repository.AddAsync(new SharedSchema { Name = "Alpha", SchemaJson = "{}" });

        var all = await _repository.ListAsync();
        Assert.Equal(2, all.Count);
        Assert.Equal("Alpha", all[0].Name);
        Assert.Equal("Bravo", all[1].Name);
    }

    [Fact]
    public async Task Update_PersistsChanges()
    {
        var id = await _repository.AddAsync(new SharedSchema { Name = "Edit", SchemaJson = "{}" });

        var loaded = await _repository.GetByIdAsync(id);
        Assert.NotNull(loaded);
        loaded!.SchemaJson = "{\"type\":\"string\"}";
        loaded.Scope = "Updated";
        await _repository.UpdateAsync(loaded);

        var reloaded = await _repository.GetByIdAsync(id);
        Assert.NotNull(reloaded);
        Assert.Equal("{\"type\":\"string\"}", reloaded!.SchemaJson);
        Assert.Equal("Updated", reloaded.Scope);
    }

    [Fact]
    public async Task Delete_RemovesRow()
    {
        var id = await _repository.AddAsync(new SharedSchema { Name = "ToDelete", SchemaJson = "{}" });

        await _repository.DeleteAsync(id);

        Assert.Null(await _repository.GetByIdAsync(id));
    }
}
