using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using foodmarket.Infrastructure.Persistence;

#nullable disable

namespace foodmarket.Infrastructure.Persistence.Migrations
{
    /// <summary>Phase5c — рефакторинг справочника единиц измерения в глобальный.
    /// До: каждая орга держала свои 5 копий («штука», «кг», ...). 95 строк
    /// в БД на 19 орг — duplication, и редактирует их кто угодно.
    /// После: 5 globals (OrganizationId=NULL), CRUD только у SuperAdmin.
    /// Орга включает нужные единицы через junction org_units_of_measure.
    ///
    /// Миграция данных:
    /// 1. По одной строке на каждую (Code, Name) пару поднимается в global
    ///    (OrganizationId→NULL).
    /// 2. Junction наполняется: каждая орга получает запись о включённости
    ///    того global'а, чью (Code, Name) она раньше держала локально.
    /// 3. products.UnitOfMeasureId remap'ится с tenant-row на global.
    /// 4. Оставшиеся tenant-rows (дубликаты) удаляются.
    /// 5. Если в БД не было какого-то канонического (Code, Name), он
    ///    добавляется явно.
    ///
    /// Безопасно для prod: products FK (OnDelete=Restrict) не падает
    /// благодаря шагу 3 перед DELETE на шаге 4.</summary>
    [DbContext(typeof(AppDbContext))]
    [Migration("20260508000000_Phase5c_UnitsOfMeasureGlobal")]
    public partial class Phase5c_UnitsOfMeasureGlobal : Migration
    {
        protected override void Up(MigrationBuilder b)
        {
            // 0. IsActive (default true) — для будущего soft-delete.
            b.AddColumn<bool>(
                name: "IsActive",
                schema: "public",
                table: "units_of_measure",
                type: "boolean",
                nullable: false,
                defaultValue: true);

            // 1. Старый unique index (OrganizationId, Code) — больше не нужен.
            b.DropIndex(
                name: "IX_units_of_measure_OrganizationId_Code",
                schema: "public",
                table: "units_of_measure");

            // 2. Поднять одну строку на (Code, Name) пару в global.
            b.Sql(@"
                WITH first_per_pair AS (
                    SELECT DISTINCT ON (""Code"", ""Name"") ""Id""
                    FROM public.units_of_measure
                    WHERE ""OrganizationId"" IS NOT NULL
                    ORDER BY ""Code"", ""Name"", ""Id""
                )
                UPDATE public.units_of_measure
                SET ""OrganizationId"" = NULL
                WHERE ""Id"" IN (SELECT ""Id"" FROM first_per_pair);");

            // 3. Создать junction org_units_of_measure.
            b.CreateTable(
                name: "org_units_of_measure",
                schema: "public",
                columns: table => new
                {
                    OrganizationId = table.Column<System.Guid>(type: "uuid", nullable: false),
                    UnitOfMeasureId = table.Column<System.Guid>(type: "uuid", nullable: false),
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_org_units_of_measure", x => new { x.OrganizationId, x.UnitOfMeasureId });
                    table.ForeignKey(
                        name: "FK_org_units_of_measure_units_of_measure_UnitOfMeasureId",
                        column: x => x.UnitOfMeasureId,
                        principalSchema: "public",
                        principalTable: "units_of_measure",
                        principalColumn: "Id",
                        onDelete: Microsoft.EntityFrameworkCore.Migrations.ReferentialAction.Restrict);
                });

            b.CreateIndex(
                name: "IX_org_units_of_measure_UnitOfMeasureId",
                schema: "public",
                table: "org_units_of_measure",
                column: "UnitOfMeasureId");

            // 4. Заполнить junction: для каждой орги — её активные globals
            //    (через сравнение Code+Name).
            b.Sql(@"
                INSERT INTO public.org_units_of_measure (""OrganizationId"", ""UnitOfMeasureId"")
                SELECT DISTINCT t.""OrganizationId"", g.""Id""
                FROM public.units_of_measure t
                JOIN public.units_of_measure g
                  ON g.""OrganizationId"" IS NULL
                 AND g.""Code"" = t.""Code""
                 AND g.""Name"" = t.""Name""
                WHERE t.""OrganizationId"" IS NOT NULL
                ON CONFLICT DO NOTHING;");

            // 5. Remap products.UnitOfMeasureId с tenant-row на global.
            b.Sql(@"
                UPDATE public.products p
                SET ""UnitOfMeasureId"" = g.""Id""
                FROM public.units_of_measure t, public.units_of_measure g
                WHERE p.""UnitOfMeasureId"" = t.""Id""
                  AND t.""OrganizationId"" IS NOT NULL
                  AND g.""OrganizationId"" IS NULL
                  AND g.""Code"" = t.""Code""
                  AND g.""Name"" = t.""Name"";");

            // 6. Удалить tenant-row дубликаты (на globals никто уже не ссылается
            //    напрямую кроме junction и products — те remap'нуты выше).
            b.Sql(@"DELETE FROM public.units_of_measure WHERE ""OrganizationId"" IS NOT NULL;");

            // 7. Доинсёртить канонические globals, если каких-то не было в БД.
            b.Sql(@"
                INSERT INTO public.units_of_measure (""Id"", ""Code"", ""Name"", ""IsActive"")
                SELECT gen_random_uuid(), v.code, v.name, true
                FROM (VALUES
                    ('796','штука'),
                    ('166','килограмм'),
                    ('112','литр'),
                    ('006','метр'),
                    ('625','упаковка')
                ) AS v(code, name)
                WHERE NOT EXISTS (
                    SELECT 1 FROM public.units_of_measure
                    WHERE ""OrganizationId"" IS NULL AND ""Code"" = v.code
                );");

            // 8. Новый unique index на Code среди active globals.
            b.CreateIndex(
                name: "IX_units_of_measure_Code",
                schema: "public",
                table: "units_of_measure",
                column: "Code",
                unique: true,
                filter: "\"IsActive\" = true");
        }

        protected override void Down(MigrationBuilder b)
        {
            b.DropIndex(
                name: "IX_units_of_measure_Code",
                schema: "public",
                table: "units_of_measure");

            b.DropTable(
                name: "org_units_of_measure",
                schema: "public");

            b.DropColumn(
                name: "IsActive",
                schema: "public",
                table: "units_of_measure");

            // Восстановить старый unique index. Данные не возвращаем — это
            // одностороння миграция (rollback вернёт лишь схему).
            b.CreateIndex(
                name: "IX_units_of_measure_OrganizationId_Code",
                schema: "public",
                table: "units_of_measure",
                columns: new[] { "OrganizationId", "Code" },
                unique: true);
        }
    }
}
