using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MaBibli.Api.Data.Migrations
{
    /// <summary>
    /// Sort le statut de lecture et l'auteur de la table <c>Livres</c>.
    /// </summary>
    /// <remarks>
    /// <b>Ce que deviennent les données existantes.</b>
    /// <list type="bullet">
    /// <item>
    /// <c>Livres.Statut</c> était commun à tout le foyer. Il est <b>rattaché à
    /// <c>AjoutePar</c></b>, la seule personne que la base associe au livre. Les statuts des
    /// livres sans <c>AjoutePar</c> (saisis sans identité) sont <b>perdus</b> : les attribuer à
    /// quelqu'un serait une invention, et les garder pour tout le monde serait exactement le
    /// défaut que cette migration corrige. Les trois valeurs sont reprises telles quelles, « À
    /// lire » compris : c'est ce que l'ancienne interface affichait, et le taire changerait ce
    /// que l'utilisateur voit.
    /// </item>
    /// <item>
    /// <c>Livres.Auteur</c> devient une ligne de <c>Auteurs</c> par valeur distincte, plus un lien
    /// en position 0. Le regroupement se fait ici sur une simple mise en minuscules — SQLite ne
    /// sait pas retirer les accents. La normalisation complète, et la fusion des variantes qu'elle
    /// révèle, sont faites en C# au démarrage suivant (voir <c>ServiceRenormalisation</c>).
    /// </item>
    /// </list>
    /// L'ordre des opérations compte : tout est recopié <b>avant</b> que les anciennes colonnes
    /// ne soient supprimées.
    /// </remarks>
    public partial class StatutPersonnelEtTableAuteurs : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            // Mise à l'abri du contenu des deux colonnes condamnées. Supprimer une colonne sous
            // SQLite reconstruit la table : on le fait pendant que Livres n'a pas encore
            // d'enfants à préserver, et on recopie ensuite depuis cette table de transit.
            migrationBuilder.Sql(
                """
                CREATE TABLE _RepriseLivres AS
                SELECT Id, Auteur, Statut FROM Livres;
                """);

            migrationBuilder.DropColumn(
                name: "Auteur",
                table: "Livres");

            migrationBuilder.DropColumn(
                name: "Statut",
                table: "Livres");

            migrationBuilder.AddColumn<string>(
                name: "TitreNormalise",
                table: "Livres",
                type: "TEXT",
                nullable: false,
                defaultValue: "");

            migrationBuilder.CreateTable(
                name: "Auteurs",
                columns: table => new
                {
                    Id = table.Column<int>(type: "INTEGER", nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
                    Nom = table.Column<string>(type: "TEXT", nullable: false),
                    NomNormalise = table.Column<string>(type: "TEXT", nullable: false),
                    CleRegroupement = table.Column<string>(type: "TEXT", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Auteurs", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "RapprochementsRefuses",
                columns: table => new
                {
                    Id = table.Column<int>(type: "INTEGER", nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
                    AuteurAId = table.Column<int>(type: "INTEGER", nullable: false),
                    AuteurBId = table.Column<int>(type: "INTEGER", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_RapprochementsRefuses", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "StatutsLecture",
                columns: table => new
                {
                    Id = table.Column<int>(type: "INTEGER", nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
                    LivreId = table.Column<int>(type: "INTEGER", nullable: false),
                    Utilisateur = table.Column<string>(type: "TEXT", nullable: false),
                    Statut = table.Column<int>(type: "INTEGER", nullable: false),
                    DateMaj = table.Column<DateTime>(type: "TEXT", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_StatutsLecture", x => x.Id);
                    table.ForeignKey(
                        name: "FK_StatutsLecture_Livres_LivreId",
                        column: x => x.LivreId,
                        principalTable: "Livres",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateTable(
                name: "LivreAuteurs",
                columns: table => new
                {
                    LivreId = table.Column<int>(type: "INTEGER", nullable: false),
                    AuteurId = table.Column<int>(type: "INTEGER", nullable: false),
                    Position = table.Column<int>(type: "INTEGER", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_LivreAuteurs", x => new { x.LivreId, x.AuteurId });
                    table.ForeignKey(
                        name: "FK_LivreAuteurs_Auteurs_AuteurId",
                        column: x => x.AuteurId,
                        principalTable: "Auteurs",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                    table.ForeignKey(
                        name: "FK_LivreAuteurs_Livres_LivreId",
                        column: x => x.LivreId,
                        principalTable: "Livres",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_Livres_TitreNormalise",
                table: "Livres",
                column: "TitreNormalise");

            migrationBuilder.CreateIndex(
                name: "IX_Auteurs_CleRegroupement",
                table: "Auteurs",
                column: "CleRegroupement",
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_Auteurs_NomNormalise",
                table: "Auteurs",
                column: "NomNormalise");

            migrationBuilder.CreateIndex(
                name: "IX_LivreAuteurs_AuteurId",
                table: "LivreAuteurs",
                column: "AuteurId");

            migrationBuilder.CreateIndex(
                name: "IX_RapprochementsRefuses_AuteurAId_AuteurBId",
                table: "RapprochementsRefuses",
                columns: new[] { "AuteurAId", "AuteurBId" },
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_StatutsLecture_LivreId_Utilisateur",
                table: "StatutsLecture",
                columns: new[] { "LivreId", "Utilisateur" },
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_StatutsLecture_Utilisateur",
                table: "StatutsLecture",
                column: "Utilisateur");

            // ── Reprise des données depuis la table de transit ──────────────────────────────

            // Le titre normalisé n'est qu'approché ici (lower() de SQLite ne touche pas aux
            // accents) ; le passage C# du démarrage le recalculera correctement.
            migrationBuilder.Sql("UPDATE Livres SET TitreNormalise = lower(trim(Titre));");

            // Une fiche auteur par valeur distincte, à la casse près. Le nom d'affichage retenu
            // est le premier dans l'ordre alphabétique : arbitraire, mais déterministe, et
            // l'utilisateur peut le corriger depuis n'importe quel livre.
            migrationBuilder.Sql(
                """
                INSERT INTO Auteurs (Nom, NomNormalise, CleRegroupement)
                SELECT min(trim(Auteur)), lower(trim(Auteur)), lower(trim(Auteur))
                FROM _RepriseLivres
                WHERE Auteur IS NOT NULL AND trim(Auteur) <> ''
                GROUP BY lower(trim(Auteur));
                """);

            migrationBuilder.Sql(
                """
                INSERT INTO LivreAuteurs (LivreId, AuteurId, Position)
                SELECT r.Id, a.Id, 0
                FROM _RepriseLivres r
                JOIN Auteurs a ON a.CleRegroupement = lower(trim(r.Auteur))
                JOIN Livres l ON l.Id = r.Id
                WHERE r.Auteur IS NOT NULL AND trim(r.Auteur) <> '';
                """);

            // Le statut commun devient celui de la personne qui a saisi le livre. Sans AjoutePar,
            // il n'y a personne à qui l'attribuer : la ligne n'est pas créée.
            migrationBuilder.Sql(
                """
                INSERT INTO StatutsLecture (LivreId, Utilisateur, Statut, DateMaj)
                SELECT l.Id, trim(l.AjoutePar), r.Statut, l.DateAjout
                FROM Livres l
                JOIN _RepriseLivres r ON r.Id = l.Id
                WHERE l.AjoutePar IS NOT NULL AND trim(l.AjoutePar) <> '';
                """);

            migrationBuilder.Sql("DROP TABLE _RepriseLivres;");
        }

        /// <inheritdoc />
        /// <summary>
        /// Retour en arrière. <b>Une partie de l'information est perdue par nature</b> : l'ancien
        /// modèle n'a qu'une case pour un auteur et une pour un statut. On y remet le premier
        /// auteur et le statut de <c>AjoutePar</c> ; les co-auteurs et les statuts des autres
        /// membres du foyer disparaissent.
        /// </summary>
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AddColumn<string>(
                name: "Auteur",
                table: "Livres",
                type: "TEXT",
                nullable: true);

            migrationBuilder.AddColumn<int>(
                name: "Statut",
                table: "Livres",
                type: "INTEGER",
                nullable: false,
                defaultValue: 0);

            migrationBuilder.Sql(
                """
                UPDATE Livres SET Auteur = (
                    SELECT a.Nom FROM LivreAuteurs la
                    JOIN Auteurs a ON a.Id = la.AuteurId
                    WHERE la.LivreId = Livres.Id
                    ORDER BY la.Position LIMIT 1);
                """);

            migrationBuilder.Sql(
                """
                UPDATE Livres SET Statut = coalesce((
                    SELECT s.Statut FROM StatutsLecture s
                    WHERE s.LivreId = Livres.Id AND s.Utilisateur = trim(Livres.AjoutePar)
                    LIMIT 1), 0);
                """);

            migrationBuilder.DropTable(
                name: "LivreAuteurs");

            migrationBuilder.DropTable(
                name: "RapprochementsRefuses");

            migrationBuilder.DropTable(
                name: "StatutsLecture");

            migrationBuilder.DropTable(
                name: "Auteurs");

            migrationBuilder.DropIndex(
                name: "IX_Livres_TitreNormalise",
                table: "Livres");

            migrationBuilder.DropColumn(
                name: "TitreNormalise",
                table: "Livres");
        }
    }
}
