using Baya.Application.Contracts.Common;
using Baya.Domain.Entities.User;
using Baya.Infrastructure.CrossCutting.Seams;
using Baya.Infrastructure.Persistence;
using Baya.Infrastructure.Persistence.Interceptors;
using Baya.Tests.Setup.Setups;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;

namespace Baya.Test.Foundation.Marketplace;

/// <summary>
/// Spins up a real <see cref="ApplicationDbContext"/> over an isolated in-memory SQLite database with the
/// audit interceptor wired and the marketplace seed applied (via <c>EnsureCreated</c>). Gives the
/// platform-signal services something faithful to run against, with a controllable clock and caller.
/// </summary>
internal sealed class OpsTestHost : IDisposable
{
    private readonly SqliteConnection _connection;

    public ApplicationDbContext Db { get; }
    public TestClock Clock { get; } = new();
    public TestCurrentUser CurrentUser { get; } = new();
    public ICacheService Cache { get; } = new MemoryCacheService(new MemoryCache(new MemoryCacheOptions()));

    public OpsTestHost()
    {
        _connection = new SqliteConnection("DataSource=:memory:");
        _connection.Open();

        Clock.UtcNow = new DateTimeOffset(2026, 7, 1, 12, 0, 0, TimeSpan.Zero);

        var interceptor = new AuditFieldInterceptor(CurrentUser, Clock);
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseSqlite(_connection)
            .AddInterceptors(interceptor)
            .Options;

        Db = new ApplicationDbContext(options, TestFieldEncryptor.Instance);
        Db.Database.EnsureCreated();
    }

    /// <summary>Adds a real user row so FK-bound rows (notifications, audit actor) satisfy the constraint.</summary>
    public async Task<int> AddUserAsync(string userName)
    {
        var user = new User { UserName = userName };
        Db.Set<User>().Add(user);
        await Db.SaveChangesAsync();
        return user.Id;
    }

    public void Dispose()
    {
        Db.Dispose();
        _connection.Dispose();
    }
}

internal sealed class TestClock : IDateTimeProvider
{
    public DateTimeOffset UtcNow { get; set; }
}

internal sealed class TestCurrentUser : ICurrentUser
{
    public int? UserId { get; set; }
    public bool IsAuthenticated => UserId is not null;
    public IReadOnlyList<string> Roles { get; set; } = [];
    public string? IpAddress { get; set; }
}
