using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;

#nullable disable

namespace ERPCore.Infra.Persistence.Migrations
{
    /// <summary>
    /// Adds the Brand / SubCategory / ItemType masters and the singleton product config,
    /// and converts CATEGORY from a self-nesting tree into a fixed two-level
    /// Category → SubCategory hierarchy (docs/10 Part C.1).
    /// <para>
    /// <b>This migration carries data, not just DDL.</b> The scaffolded version dropped
    /// <c>categories.ParentId</c> outright, which would have silently flattened every
    /// child category into a root and left items pointing at what is now a top-level
    /// category — losing the parent entirely. The hand-written steps below (marked
    /// "data migration") move child categories into <c>subcategories</c> and repoint items
    /// onto the correct (category, subcategory) pair before the column goes away.
    /// </para>
    /// </summary>
    public partial class AddBrandsSubcategoriesItemTypesAndProductConfig : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            // NOTE: the ParentId drop is deliberately deferred to the bottom of this method —
            // the data migration reads it. Order here is load-bearing.
            migrationBuilder.RenameColumn(
                name: "ItemType",
                table: "items",
                newName: "StockNature");

            migrationBuilder.AddColumn<int>(
                name: "BrandId",
                table: "items",
                type: "integer",
                nullable: true);

            migrationBuilder.AddColumn<int>(
                name: "SubCategoryId",
                table: "items",
                type: "integer",
                nullable: true);

            migrationBuilder.AddColumn<DateTime>(
                name: "CreatedAt",
                table: "categories",
                type: "timestamp with time zone",
                nullable: false,
                defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));

            migrationBuilder.AddColumn<string>(
                name: "Status",
                table: "categories",
                type: "character varying(20)",
                maxLength: 20,
                nullable: false,
                defaultValue: "Active");

            migrationBuilder.AddColumn<DateTime>(
                name: "UpdatedAt",
                table: "categories",
                type: "timestamp with time zone",
                nullable: true);

            migrationBuilder.AddColumn<uint>(
                name: "xmin",
                table: "categories",
                type: "xid",
                rowVersion: true,
                nullable: false,
                defaultValue: 0u);

            migrationBuilder.CreateTable(
                name: "brands",
                columns: table => new
                {
                    BrandId = table.Column<int>(type: "integer", nullable: false)
                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
                    Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
                    Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false, defaultValue: "Active"),
                    CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
                    UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
                    xmin = table.Column<uint>(type: "xid", rowVersion: true, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_brands", x => x.BrandId);
                });

            migrationBuilder.CreateTable(
                name: "item_types",
                columns: table => new
                {
                    ItemTypeId = table.Column<int>(type: "integer", nullable: false)
                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
                    Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
                    Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false, defaultValue: "Active"),
                    CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
                    UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
                    xmin = table.Column<uint>(type: "xid", rowVersion: true, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_item_types", x => x.ItemTypeId);
                });

            migrationBuilder.CreateTable(
                name: "product_config",
                columns: table => new
                {
                    ConfigId = table.Column<int>(type: "integer", nullable: false),
                    SubcategoriesEnabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true),
                    BrandsEnabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true),
                    ItemTypesEnabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true),
                    UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
                    UpdatedBy = table.Column<int>(type: "integer", nullable: true),
                    xmin = table.Column<uint>(type: "xid", rowVersion: true, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_product_config", x => x.ConfigId);
                    table.CheckConstraint("ck_product_config_singleton", "\"ConfigId\" = 1");
                    table.ForeignKey(
                        name: "FK_product_config_users_UpdatedBy",
                        column: x => x.UpdatedBy,
                        principalTable: "users",
                        principalColumn: "UserId",
                        onDelete: ReferentialAction.Restrict);
                });

            migrationBuilder.CreateTable(
                name: "subcategories",
                columns: table => new
                {
                    SubCategoryId = table.Column<int>(type: "integer", nullable: false)
                        .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
                    Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
                    CategoryId = table.Column<int>(type: "integer", nullable: false),
                    Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false, defaultValue: "Active"),
                    CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
                    UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
                    xmin = table.Column<uint>(type: "xid", rowVersion: true, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_subcategories", x => x.SubCategoryId);
                    table.ForeignKey(
                        name: "FK_subcategories_categories_CategoryId",
                        column: x => x.CategoryId,
                        principalTable: "categories",
                        principalColumn: "CategoryId",
                        onDelete: ReferentialAction.Restrict);
                });

            // ---------------------------------------------------------------------------
            // DATA MIGRATION — must run before ParentId is dropped.
            // ---------------------------------------------------------------------------

            // Existing categories predate CreatedAt; the added column defaulted them to
            // 0001-01-01. Stamp them with the migration time instead of a sentinel date.
            migrationBuilder.Sql(@"
                UPDATE categories SET ""CreatedAt"" = NOW() AT TIME ZONE 'utc';
            ");

            // Carry the old category id alongside each new subcategory so items can be
            // repointed by join below. Dropped again once the repoint is done.
            migrationBuilder.Sql(@"
                ALTER TABLE subcategories ADD COLUMN legacy_category_id integer;
            ");

            // Walk the old tree to its roots. The previous model allowed unlimited nesting,
            // but the new one is exactly two levels — so a category at any depth below the
            // root collapses into a subcategory of its ROOT ancestor (a grandchild cannot
            // become a subcategory of its immediate parent, since that parent is itself
            // ceasing to be a category).
            migrationBuilder.Sql(@"
                WITH RECURSIVE tree AS (
                    SELECT ""CategoryId"", ""ParentId"", ""Name"", ""CategoryId"" AS root_id
                    FROM categories
                    WHERE ""ParentId"" IS NULL
                    UNION ALL
                    SELECT c.""CategoryId"", c.""ParentId"", c.""Name"", t.root_id
                    FROM categories c
                    JOIN tree t ON c.""ParentId"" = t.""CategoryId""
                )
                INSERT INTO subcategories (""Name"", ""CategoryId"", ""Status"", ""CreatedAt"", legacy_category_id)
                SELECT t.""Name"", t.root_id, 'Active', NOW() AT TIME ZONE 'utc', t.""CategoryId""
                FROM tree t
                WHERE t.""ParentId"" IS NOT NULL;
            ");

            // Repoint items: an item that pointed at a child category now carries the root
            // category plus the subcategory it actually meant.
            migrationBuilder.Sql(@"
                UPDATE items i
                SET ""SubCategoryId"" = s.""SubCategoryId"",
                    ""CategoryId""    = s.""CategoryId""
                FROM subcategories s
                WHERE s.legacy_category_id = i.""CategoryId"";
            ");

            // The self-FK must go before the delete, or RESTRICT rejects removing a parent
            // whose own child row is still present.
            migrationBuilder.DropForeignKey(
                name: "FK_categories_categories_ParentId",
                table: "categories");

            // Every non-root category now lives in `subcategories`, and no item references
            // one any more (repointed above), so the rows can go.
            migrationBuilder.Sql(@"
                DELETE FROM categories WHERE ""ParentId"" IS NOT NULL;
                ALTER TABLE subcategories DROP COLUMN legacy_category_id;
            ");

            migrationBuilder.DropIndex(
                name: "IX_categories_ParentId",
                table: "categories");

            migrationBuilder.DropColumn(
                name: "ParentId",
                table: "categories");

            // Seed the singleton config (FR-MD-11) — all features on. Item writes read this
            // row, so it must exist before the app serves a single request.
            migrationBuilder.Sql(@"
                INSERT INTO product_config (""ConfigId"", ""SubcategoriesEnabled"", ""BrandsEnabled"", ""ItemTypesEnabled"")
                VALUES (1, TRUE, TRUE, TRUE)
                ON CONFLICT (""ConfigId"") DO NOTHING;
            ");

            // ---------------------------------------------------------------------------

            migrationBuilder.CreateIndex(
                name: "IX_items_BrandId",
                table: "items",
                column: "BrandId");

            migrationBuilder.CreateIndex(
                name: "IX_items_SubCategoryId",
                table: "items",
                column: "SubCategoryId");

            migrationBuilder.CreateIndex(
                name: "IX_categories_Name",
                table: "categories",
                column: "Name",
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_categories_Status",
                table: "categories",
                column: "Status");

            migrationBuilder.CreateIndex(
                name: "IX_brands_Name",
                table: "brands",
                column: "Name",
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_brands_Status",
                table: "brands",
                column: "Status");

            migrationBuilder.CreateIndex(
                name: "IX_item_types_Name",
                table: "item_types",
                column: "Name",
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_item_types_Status",
                table: "item_types",
                column: "Status");

            migrationBuilder.CreateIndex(
                name: "IX_product_config_UpdatedBy",
                table: "product_config",
                column: "UpdatedBy");

            migrationBuilder.CreateIndex(
                name: "IX_subcategories_CategoryId_Name",
                table: "subcategories",
                columns: new[] { "CategoryId", "Name" },
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_subcategories_Status",
                table: "subcategories",
                column: "Status");

            migrationBuilder.AddForeignKey(
                name: "FK_items_brands_BrandId",
                table: "items",
                column: "BrandId",
                principalTable: "brands",
                principalColumn: "BrandId",
                onDelete: ReferentialAction.Restrict);

            migrationBuilder.AddForeignKey(
                name: "FK_items_subcategories_SubCategoryId",
                table: "items",
                column: "SubCategoryId",
                principalTable: "subcategories",
                principalColumn: "SubCategoryId",
                onDelete: ReferentialAction.Restrict);
        }

        /// <summary>
        /// Reverses the schema change and puts the subcategory data back where it came from.
        /// <para>
        /// The scaffolded version simply dropped <c>subcategories</c>, which would have
        /// discarded exactly what <see cref="Up"/> preserved. Instead each subcategory is
        /// restored as a child category and its items are repointed back onto it. This is
        /// not perfectly lossless: the old tree's depth is gone (a former grandchild comes
        /// back as a direct child of its root), and Brand data cannot survive a schema that
        /// has nowhere to put it.
        /// </para>
        /// </summary>
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropForeignKey(
                name: "FK_items_brands_BrandId",
                table: "items");

            migrationBuilder.DropForeignKey(
                name: "FK_items_subcategories_SubCategoryId",
                table: "items");

            // Restore the parent column + self-FK first so subcategories have somewhere to
            // land, then move them back before the table is dropped.
            migrationBuilder.AddColumn<int>(
                name: "ParentId",
                table: "categories",
                type: "integer",
                nullable: true);

            // ---------------------------------------------------------------------------
            // DATA MIGRATION (reverse) — must run before `subcategories` is dropped.
            // ---------------------------------------------------------------------------

            migrationBuilder.Sql(@"
                ALTER TABLE categories ADD COLUMN legacy_subcategory_id integer;
            ");

            // Each subcategory becomes a child category again under the same parent.
            migrationBuilder.Sql(@"
                INSERT INTO categories (""Name"", ""ParentId"", ""CreatedAt"", ""Status"", legacy_subcategory_id)
                SELECT s.""Name"", s.""CategoryId"", s.""CreatedAt"", s.""Status"", s.""SubCategoryId""
                FROM subcategories s;
            ");

            // Items that carried a subcategory point back at the restored child category.
            migrationBuilder.Sql(@"
                UPDATE items i
                SET ""CategoryId"" = c.""CategoryId""
                FROM categories c
                WHERE c.legacy_subcategory_id = i.""SubCategoryId"";
            ");

            migrationBuilder.Sql(@"
                ALTER TABLE categories DROP COLUMN legacy_subcategory_id;
            ");

            // ---------------------------------------------------------------------------

            migrationBuilder.DropTable(
                name: "brands");

            migrationBuilder.DropTable(
                name: "item_types");

            migrationBuilder.DropTable(
                name: "product_config");

            migrationBuilder.DropTable(
                name: "subcategories");

            migrationBuilder.DropIndex(
                name: "IX_items_BrandId",
                table: "items");

            migrationBuilder.DropIndex(
                name: "IX_items_SubCategoryId",
                table: "items");

            migrationBuilder.DropIndex(
                name: "IX_categories_Name",
                table: "categories");

            migrationBuilder.DropIndex(
                name: "IX_categories_Status",
                table: "categories");

            migrationBuilder.DropColumn(
                name: "BrandId",
                table: "items");

            migrationBuilder.DropColumn(
                name: "SubCategoryId",
                table: "items");

            migrationBuilder.DropColumn(
                name: "CreatedAt",
                table: "categories");

            migrationBuilder.DropColumn(
                name: "Status",
                table: "categories");

            migrationBuilder.DropColumn(
                name: "UpdatedAt",
                table: "categories");

            migrationBuilder.DropColumn(
                name: "xmin",
                table: "categories");

            migrationBuilder.RenameColumn(
                name: "StockNature",
                table: "items",
                newName: "ItemType");

            // ParentId itself was re-added at the top of this method, ahead of the reverse
            // data migration that populates it.
            migrationBuilder.CreateIndex(
                name: "IX_categories_ParentId",
                table: "categories",
                column: "ParentId");

            migrationBuilder.AddForeignKey(
                name: "FK_categories_categories_ParentId",
                table: "categories",
                column: "ParentId",
                principalTable: "categories",
                principalColumn: "CategoryId",
                onDelete: ReferentialAction.Restrict);
        }
    }
}
