using Microsoft.AspNetCore.DataProtection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Deployment;
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase;

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

/// <summary>
/// Test DbContext that adapts SQL Server-specific features for SQLite:
/// - Maps DateTimeOffset to sortable ISO 8601 strings (SQLite has no native DateTimeOffset ORDER BY)
/// - Replaces SQL Server RowVersion with a nullable byte[] column (SQLite can't auto-generate rowversion)
///
/// Constructed with an explicit ephemeral Data Protection provider so secret-bearing
/// columns are write-capable in tests. The schema-only no-provider constructor would
/// throw on a secret-column write (ConfigurationDatabase-013); passing a provider here
/// makes the test fixture's intent explicit at the call site.
/// </summary>
public class SqliteTestDbContext : ScadaBridgeDbContext
{
    public SqliteTestDbContext(DbContextOptions<ScadaBridgeDbContext> options)
        : base(options, new EphemeralDataProtectionProvider())
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // SQLite cannot auto-generate SQL Server rowversion values.
        // Replace with a nullable byte[] column so inserts don't fail with NOT NULL constraint.
        modelBuilder.Entity<DeploymentRecord>(builder =>
        {
            builder.Property(d => d.RowVersion)
                .IsRequired(false)
                .IsConcurrencyToken(false)
                .ValueGeneratedNever();
        });

        // Note (C5 / Task 2.5): the central dbo.AuditLog persisted computed columns
        // (JSON_VALUE-based) are neutralized for non-SQL-Server providers inside
        // ScadaBridgeDbContext.OnModelCreating itself, so every SQLite test context —
        // including ones that construct ScadaBridgeDbContext directly rather than via
        // this helper — gets the strip. Nothing to do here.

        // Convert DateTimeOffset to ISO 8601 string for SQLite so ORDER BY works
        var converter = new ValueConverter<DateTimeOffset, string>(
            v => v.UtcDateTime.ToString("o"),
            v => DateTimeOffset.Parse(v));

        var nullableConverter = new ValueConverter<DateTimeOffset?, string?>(
            v => v.HasValue ? v.Value.UtcDateTime.ToString("o") : null,
            v => v != null ? DateTimeOffset.Parse(v) : null);

        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            foreach (var property in entityType.GetProperties())
            {
                if (property.ClrType == typeof(DateTimeOffset))
                {
                    property.SetValueConverter(converter);
                    property.SetColumnType("TEXT");
                }
                else if (property.ClrType == typeof(DateTimeOffset?))
                {
                    property.SetValueConverter(nullableConverter);
                    property.SetColumnType("TEXT");
                }
            }
        }
    }
}

public static class SqliteTestHelper
{
    public static ScadaBridgeDbContext CreateInMemoryContext()
    {
        var options = new DbContextOptionsBuilder<ScadaBridgeDbContext>()
            .UseSqlite("DataSource=:memory:")
            .ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning))
            .Options;

        var context = new SqliteTestDbContext(options);
        context.Database.OpenConnection();
        context.Database.EnsureCreated();
        return context;
    }

    /// <summary>
    /// In-memory SQLite context with EF command interceptors attached — lets a
    /// test observe the actual SQL a repository emits (e.g. count DELETE statements
    /// issued by a batched purge).
    /// </summary>
    public static ScadaBridgeDbContext CreateInMemoryContext(params IInterceptor[] interceptors)
    {
        var options = new DbContextOptionsBuilder<ScadaBridgeDbContext>()
            .UseSqlite("DataSource=:memory:")
            .ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning))
            .AddInterceptors(interceptors)
            .Options;

        var context = new SqliteTestDbContext(options);
        context.Database.OpenConnection();
        context.Database.EnsureCreated();
        return context;
    }

    public static ScadaBridgeDbContext CreateFileContext(string dbPath)
    {
        var options = new DbContextOptionsBuilder<ScadaBridgeDbContext>()
            .UseSqlite($"DataSource={dbPath}")
            .ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning))
            .Options;

        var context = new SqliteTestDbContext(options);
        return context;
    }
}
