"""series chapters become cosmetic dividers; pages become one series-global run

FC-6.x reframe (#789). A series is now ONE flat, series-global ordered run of
pages; chapters stop owning pages and become labeled dividers anchored to the
page that begins them.

Migration (order matters — series_page.chapter_id cascades, so it must be
dropped BEFORE any chapter row is deleted, or pages would cascade away):
  a. Renumber series_page.page_number to a series-global 1..N (ordered by the
     OLD (chapter_number, page_number)).
  b. Add series_chapter.anchor_page_id and populate it with each chapter's first
     page (lowest new page_number).
  c. Drop series_page.chapter_id (severs the cascade link).
  d. Prune chapters that shouldn't become dividers: empty/placeholder ones (no
     anchor) and the redundant unlabeled chapter that would sit at page 1.
  e. Reshape series_chapter into the divider: drop chapter_number,
     is_placeholder, stated_page_start/end; make anchor_page_id NOT NULL +
     UNIQUE + FK→series_page ON DELETE CASCADE.

Revision ID: 0047
Revises: 0046
Create Date: 2026-06-11

"""
from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

revision: str = "0047"
down_revision: Union[str, None] = "0046"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # a. series-global page numbering, preserving the old reading order.
    op.execute(
        """
        WITH ordered AS (
            SELECT sp.id,
                   ROW_NUMBER() OVER (
                       PARTITION BY sp.series_tag_id
                       ORDER BY sc.chapter_number, sp.page_number, sp.id
                   ) AS rn
            FROM series_page sp
            JOIN series_chapter sc ON sc.id = sp.chapter_id
        )
        UPDATE series_page sp
        SET page_number = ordered.rn
        FROM ordered
        WHERE sp.id = ordered.id
        """
    )

    # b. anchor each existing chapter at its first page (lowest new page_number).
    op.add_column(
        "series_chapter",
        sa.Column("anchor_page_id", sa.Integer(), nullable=True),
    )
    op.execute(
        """
        WITH firsts AS (
            SELECT DISTINCT ON (sp.chapter_id)
                   sp.chapter_id, sp.id AS page_id
            FROM series_page sp
            ORDER BY sp.chapter_id, sp.page_number, sp.id
        )
        UPDATE series_chapter sc
        SET anchor_page_id = firsts.page_id
        FROM firsts
        WHERE firsts.chapter_id = sc.id
        """
    )

    # c. sever the ownership link (drops the FK + index with the column) BEFORE
    #    pruning chapters, so deleting a chapter can't cascade-delete its pages.
    op.drop_column("series_page", "chapter_id")

    # d. prune chapters that don't become dividers: placeholders / empty ones
    #    (no anchor), and the unlabeled chapter that would land redundantly at
    #    page 1 (the series just starts — no divider needed there).
    op.execute(
        """
        DELETE FROM series_chapter sc
        USING (
            SELECT sc2.id
            FROM series_chapter sc2
            LEFT JOIN series_page sp ON sp.id = sc2.anchor_page_id
            WHERE sc2.anchor_page_id IS NULL
               OR (sp.page_number = 1
                   AND sc2.title IS NULL
                   AND sc2.stated_part IS NULL)
        ) gone
        WHERE sc.id = gone.id
        """
    )

    # e. reshape into the divider model.
    op.drop_column("series_chapter", "chapter_number")
    op.drop_column("series_chapter", "is_placeholder")
    op.drop_column("series_chapter", "stated_page_start")
    op.drop_column("series_chapter", "stated_page_end")
    op.alter_column("series_chapter", "anchor_page_id", nullable=False)
    op.create_unique_constraint(
        "uq_series_chapter_anchor_page", "series_chapter", ["anchor_page_id"]
    )
    op.create_foreign_key(
        "fk_series_chapter_anchor_page",
        "series_chapter",
        "series_page",
        ["anchor_page_id"],
        ["id"],
        ondelete="CASCADE",
    )


def downgrade() -> None:
    # Lossy: dividers can't be reconstructed as owning chapters. Collapse back to
    # exactly one chapter per series that owns all its pages in order.
    op.add_column(
        "series_page", sa.Column("chapter_id", sa.Integer(), nullable=True)
    )
    op.drop_constraint(
        "fk_series_chapter_anchor_page", "series_chapter", type_="foreignkey"
    )
    op.drop_constraint(
        "uq_series_chapter_anchor_page", "series_chapter", type_="unique"
    )
    op.drop_column("series_chapter", "anchor_page_id")
    op.add_column(
        "series_chapter",
        sa.Column(
            "chapter_number", sa.Integer(), nullable=False, server_default="1"
        ),
    )
    op.add_column(
        "series_chapter",
        sa.Column(
            "is_placeholder", sa.Boolean(), nullable=False,
            server_default="false",
        ),
    )
    op.add_column(
        "series_chapter",
        sa.Column("stated_page_start", sa.Integer(), nullable=True),
    )
    op.add_column(
        "series_chapter",
        sa.Column("stated_page_end", sa.Integer(), nullable=True),
    )
    op.execute("DELETE FROM series_chapter")
    op.execute(
        """
        INSERT INTO series_chapter (series_tag_id, chapter_number)
        SELECT DISTINCT series_tag_id, 1 FROM series_page
        """
    )
    op.execute(
        """
        UPDATE series_page sp
        SET chapter_id = sc.id
        FROM series_chapter sc
        WHERE sc.series_tag_id = sp.series_tag_id
        """
    )
    op.alter_column("series_page", "chapter_id", nullable=False)
    op.create_foreign_key(
        "fk_series_page_chapter",
        "series_page",
        "series_chapter",
        ["chapter_id"],
        ["id"],
        ondelete="CASCADE",
    )
