using Baya.Domain.Entities.Messaging;
using Baya.Domain.Entities.User;
using Baya.Infrastructure.Persistence;
using Baya.Tests.Setup.Setups;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;

namespace Baya.Test.Foundation.Messaging;

/// <summary>
/// refinement-phase-9 §9.5: ticket message bodies (the refund/dispute paper trail — phone numbers, addresses,
/// clinical detail) are encrypted at rest through the <c>IFieldEncryptor</c> seam. The stored column holds
/// ciphertext, never the plaintext; a normal read still returns the plaintext (EF applies the converter).
/// </summary>
public sealed class TicketMessageEncryptionTests : IDisposable
{
    private static readonly DateTimeOffset Now = new(2026, 8, 10, 10, 0, 0, TimeSpan.Zero);

    private readonly SqliteConnection _connection;
    private readonly ApplicationDbContext _db;

    public TicketMessageEncryptionTests()
    {
        _connection = new SqliteConnection("DataSource=:memory:");
        _connection.Open();
        var options = new DbContextOptionsBuilder<ApplicationDbContext>().UseSqlite(_connection).Options;
        _db = new ApplicationDbContext(options, TestFieldEncryptor.Instance);
        _db.Database.EnsureCreated();
    }

    private long SeedMessage(string body)
    {
        var sender = new User { UserName = "u1", PhoneNumber = "09120000001", Gender = "male", Name = "علی", FamilyName = "رضایی", IsActive = true };
        _db.Users.Add(sender);
        _db.SaveChanges();

        var ticket = new Ticket { ReferenceCode = "TKT-ENC001", Category = TicketCategory.Support, OpenedById = sender.Id };
        _db.Set<Ticket>().Add(ticket);
        _db.SaveChanges();

        var message = new TicketMessage { TicketId = ticket.Id, SenderId = sender.Id, Body = body, IsInternal = false, SentAt = Now };
        _db.Set<TicketMessage>().Add(message);
        _db.SaveChanges();
        return message.Id;
    }

    [Fact]
    public async Task Body_is_stored_encrypted_at_rest()
    {
        const string plaintext = "patient at 09121234567, unit 4B — chest pain";
        var id = SeedMessage(plaintext);

        await using var command = _connection.CreateCommand();
        command.CommandText = "SELECT Body FROM TicketMessages WHERE Id = $id";
        command.Parameters.AddWithValue("$id", id);
        var stored = (string?)await command.ExecuteScalarAsync();

        Assert.NotNull(stored);
        Assert.NotEqual(plaintext, stored);
        Assert.Equal(TestFieldEncryptor.Instance.Encrypt(plaintext), stored);
        Assert.DoesNotContain("09121234567", stored);
    }

    [Fact]
    public async Task Body_round_trips_to_plaintext_on_read()
    {
        const string plaintext = "please confirm the visit address";
        var id = SeedMessage(plaintext);

        // Fresh context so the value comes from the store, not the change tracker.
        var options = new DbContextOptionsBuilder<ApplicationDbContext>().UseSqlite(_connection).Options;
        await using var freshDb = new ApplicationDbContext(options, TestFieldEncryptor.Instance);
        var body = await freshDb.Set<TicketMessage>().Where(m => m.Id == id).Select(m => m.Body).SingleAsync();

        Assert.Equal(plaintext, body);
    }

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