using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using ScadaLink.AuditLog.Site;

namespace ScadaLink.AuditLog.Tests.Site;

/// <summary>
/// Bundle B (M2-T1) schema-bootstrap tests for <see cref="SqliteAuditWriter"/>.
/// Uses an in-memory shared-cache SQLite database so the same connection name
/// reaches the same file-less db across both the writer and the verifier.
/// </summary>
public class SqliteAuditWriterSchemaTests
{
    /// <summary>
    /// Each test uses a unique shared-cache in-memory database. The
    /// "Mode=Memory;Cache=Shared" syntax lets two SqliteConnections see the same
    /// in-memory store as long as both use the same Data Source name.
    /// </summary>
    private static (SqliteAuditWriter writer, string dataSource) CreateWriter(string testName)
    {
        var dataSource = $"file:{testName}-{Guid.NewGuid():N}?mode=memory&cache=shared";
        var options = new SqliteAuditWriterOptions
        {
            DatabasePath = dataSource,
        };
        // The writer uses raw "Data Source={path}" by appending Cache=Shared. Override
        // by passing the full connection string via the connectionStringOverride hook.
        var writer = new SqliteAuditWriter(
            Options.Create(options),
            NullLogger<SqliteAuditWriter>.Instance,
            connectionStringOverride: $"Data Source={dataSource};Cache=Shared");
        return (writer, dataSource);
    }

    private static SqliteConnection OpenVerifierConnection(string dataSource)
    {
        var connection = new SqliteConnection($"Data Source={dataSource};Cache=Shared");
        connection.Open();
        return connection;
    }

    [Fact]
    public void Opens_Creates_AuditLog_Table_With_20Columns_And_PK_On_EventId()
    {
        var (writer, dataSource) = CreateWriter(nameof(Opens_Creates_AuditLog_Table_With_20Columns_And_PK_On_EventId));
        using (writer)
        {
            using var connection = OpenVerifierConnection(dataSource);
            using var cmd = connection.CreateCommand();
            cmd.CommandText = "PRAGMA table_info(AuditLog);";
            using var reader = cmd.ExecuteReader();

            var columns = new List<(string Name, int Pk)>();
            while (reader.Read())
            {
                columns.Add((reader.GetString(1), reader.GetInt32(5)));
            }

            Assert.Equal(20, columns.Count);

            var expected = new[]
            {
                "EventId", "OccurredAtUtc", "Channel", "Kind", "CorrelationId",
                "SourceSiteId", "SourceInstanceId", "SourceScript", "Actor", "Target",
                "Status", "HttpStatus", "DurationMs", "ErrorMessage", "ErrorDetail",
                "RequestSummary", "ResponseSummary", "PayloadTruncated", "Extra",
                "ForwardState",
            };
            Assert.Equal(expected.OrderBy(n => n), columns.Select(c => c.Name).OrderBy(n => n));

            // PK is EventId only.
            var pkColumns = columns.Where(c => c.Pk > 0).Select(c => c.Name).ToList();
            Assert.Single(pkColumns);
            Assert.Equal("EventId", pkColumns[0]);
        }
    }

    [Fact]
    public void Opens_Creates_IX_ForwardState_Occurred_Index()
    {
        var (writer, dataSource) = CreateWriter(nameof(Opens_Creates_IX_ForwardState_Occurred_Index));
        using (writer)
        {
            using var connection = OpenVerifierConnection(dataSource);
            using var cmd = connection.CreateCommand();
            cmd.CommandText = "PRAGMA index_list(AuditLog);";
            using var reader = cmd.ExecuteReader();

            var indexNames = new List<string>();
            while (reader.Read())
            {
                indexNames.Add(reader.GetString(1));
            }

            Assert.Contains("IX_SiteAuditLog_ForwardState_Occurred", indexNames);

            // Verify the index columns are ForwardState, OccurredAtUtc in that order.
            using var infoCmd = connection.CreateCommand();
            infoCmd.CommandText = "PRAGMA index_info(IX_SiteAuditLog_ForwardState_Occurred);";
            using var infoReader = infoCmd.ExecuteReader();

            var indexColumns = new List<string>();
            while (infoReader.Read())
            {
                indexColumns.Add(infoReader.GetString(2));
            }

            Assert.Equal(new[] { "ForwardState", "OccurredAtUtc" }, indexColumns);
        }
    }

    [Fact]
    public void PRAGMA_auto_vacuum_Is_INCREMENTAL()
    {
        var (writer, dataSource) = CreateWriter(nameof(PRAGMA_auto_vacuum_Is_INCREMENTAL));
        using (writer)
        {
            using var connection = OpenVerifierConnection(dataSource);
            using var cmd = connection.CreateCommand();
            cmd.CommandText = "PRAGMA auto_vacuum;";
            var value = Convert.ToInt32(cmd.ExecuteScalar());

            // INCREMENTAL = 2 (0 = NONE, 1 = FULL, 2 = INCREMENTAL).
            Assert.Equal(2, value);
        }
    }
}
