using MaBibli.Api.Data;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;

namespace MaBibli.Tests;

/// <summary>
/// La migration <c>RangDesEnvies</c>, appliquée pour de vrai à une base déjà peuplée.
/// </summary>
/// <remarks>
/// Ce test ne double pas <c>ServiceSouhaitsTests</c> : celui-ci crée son schéma avec
/// <c>EnsureCreated</c>, qui <b>ne joue aucune migration</b>. Le remplissage de la colonne
/// <c>Rang</c> n'y passerait donc jamais, et une erreur dans son SQL ne se verrait qu'en
/// production — sur les listes d'envies réelles, qui se réordonneraient sous les yeux de leur
/// propriétaire.
/// </remarks>
public class MigrationRangDesEnviesTests : IDisposable
{
    private const string AvantLeRang = "20260818052834_ListeDEnvies";

    private readonly SqliteConnection _connexion;
    private readonly MaBibliDbContext _db;

    public MigrationRangDesEnviesTests()
    {
        // Connexion maintenue ouverte : une base SQLite « :memory: » disparaît avec elle.
        _connexion = new SqliteConnection("Data Source=:memory:");
        _connexion.Open();

        _db = new MaBibliDbContext(
            new DbContextOptionsBuilder<MaBibliDbContext>().UseSqlite(_connexion).Options);
    }

    private IMigrator Migrateur => _db.GetInfrastructure().GetRequiredService<IMigrator>();

    /// <summary>
    /// Le cœur du test : la migration doit reconduire <b>l'ordre affiché jusqu'ici</b> —
    /// auteurs renseignés d'abord, puis par auteur, puis par titre — et repartir de zéro pour
    /// chaque utilisateur, le rang étant personnel comme le reste de la table.
    /// </summary>
    [Fact]
    public async Task Reconduit_lancien_ordre_daffichage_utilisateur_par_utilisateur()
    {
        await Migrateur.MigrateAsync(AvantLeRang);

        // Insertion en SQL brut : à ce point du schéma, la colonne Rang n'existe pas encore,
        // et l'entité C# la porte déjà. Passer par EF échouerait.
        await ExecuterAsync("""
            INSERT INTO LivresSouhaites
                (Id, Utilisateur, Titre, TitreNormalise, Auteur, AuteurNormalise, DateAjout)
            VALUES
                (1, 'mathieu', 'Germinal',   'germinal',   'Émile Zola',     'emile zola',     '2026-01-01'),
                (2, 'mathieu', 'Sans nom',   'sans nom',   NULL,             '',               '2026-01-02'),
                (3, 'mathieu', 'Au bonheur', 'au bonheur', 'Émile Zola',     'emile zola',     '2026-01-03'),
                (4, 'mathieu', 'Les fourmis','les fourmis','Bernard Werber', 'bernard werber', '2026-01-04'),
                (5, 'camille', 'Sa liste',   'sa liste',   'Bernard Werber', 'bernard werber', '2026-01-05'),
                (6, 'camille', 'Sans nom',   'sans nom',   NULL,             '',               '2026-01-06');
            """);

        await Migrateur.MigrateAsync();

        // Werber avant Zola, « Au bonheur » avant « Germinal », et l'envie sans auteur en
        // dernier : exactement ce que la liste montrait avant la colonne.
        Assert.Equal(
            [(0, "Les fourmis"), (1, "Au bonheur"), (2, "Germinal"), (3, "Sans nom")],
            await RangsAsync("mathieu"));

        // ⚠️ La numérotation de Camille repart de zéro : le rang n'a de sens qu'à l'intérieur
        // d'une liste. Continuer celle de Mathieu ferait dépendre l'ordre de l'un de l'autre.
        Assert.Equal([(0, "Sa liste"), (1, "Sans nom")], await RangsAsync("camille"));
    }

    /// <summary>Une base sans aucune envie doit traverser la migration sans broncher.</summary>
    [Fact]
    public async Task Sapplique_aussi_a_une_base_vide()
    {
        await Migrateur.MigrateAsync();

        Assert.Empty(await _db.LivresSouhaites.ToListAsync());
    }

    private async Task ExecuterAsync(string sql)
    {
        await using var commande = _connexion.CreateCommand();
        commande.CommandText = sql;
        await commande.ExecuteNonQueryAsync();
    }

    private async Task<List<(int Rang, string Titre)>> RangsAsync(string utilisateur)
    {
        // Contexte neuf : le précédent a pu mettre en cache le schéma d'avant la migration.
        await using var db = new MaBibliDbContext(
            new DbContextOptionsBuilder<MaBibliDbContext>().UseSqlite(_connexion).Options);

        return await db.LivresSouhaites
            .Where(s => s.Utilisateur == utilisateur)
            .OrderBy(s => s.Rang)
            .Select(s => new ValueTuple<int, string>(s.Rang, s.Titre))
            .ToListAsync();
    }

    public void Dispose()
    {
        _db.Dispose();
        _connexion.Dispose();
        GC.SuppressFinalize(this);
    }
}
