"""image_record.earliest_post_date: original-publish gallery sort key + index

Revision ID: 0071
Revises: 0070
Create Date: 2026-07-01

effective_date (0035) keys off the PRIMARY post — which is often the repost /
download the file actually came from — and falls back to created_at, so the
gallery's default order surfaces download dates rather than when content was
first posted (operator-flagged 2026-07-01). Materialize a second sort key,
earliest_post_date = MIN(post_date) across ALL of an image's provenance posts
(every post it appears in), falling back to created_at only when no linked post
carries a date. Indexed (DESC, id DESC) so the "post date" gallery sort is an
index range scan just like effective_date.

Backfill mirrors 0035: created_at baseline, then override with the MIN over
image_provenance ⋈ post. New rows get the created_at-equivalent server default;
services/importer.py recomputes it whenever a dated post is linked.
"""
from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

revision: str = "0071"
down_revision: Union[str, None] = "0070"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # Add nullable first so the backfill can populate before NOT NULL.
    op.add_column(
        "image_record",
        sa.Column("earliest_post_date", sa.DateTime(timezone=True), nullable=True),
    )
    # Baseline: download date. Set-based (no per-row binds) → immune to the
    # 65535 bind-parameter ceiling regardless of library size.
    op.execute(
        """
        UPDATE image_record
        SET earliest_post_date = created_at
        """
    )
    # Override with the earliest post_date across EVERY post the image appears
    # in (image_provenance is the many-to-many edge; ignore posts with no date).
    op.execute(
        """
        UPDATE image_record AS ir
        SET earliest_post_date = sub.min_date
        FROM (
            SELECT ip.image_record_id AS iid, MIN(p.post_date) AS min_date
            FROM image_provenance AS ip
            JOIN post AS p ON p.id = ip.post_id
            WHERE p.post_date IS NOT NULL
            GROUP BY ip.image_record_id
        ) AS sub
        WHERE ir.id = sub.iid
        """
    )
    op.alter_column(
        "image_record",
        "earliest_post_date",
        nullable=False,
        server_default=sa.text("now()"),
    )
    # DESC/DESC matches the gallery's ORDER BY earliest_post_date DESC, id DESC
    # so the "post date" scroll is a forward index scan; raw SQL because
    # alembic's column list doesn't express per-column DESC cleanly.
    op.execute(
        "CREATE INDEX ix_image_record_earliest_post_date "
        "ON image_record (earliest_post_date DESC, id DESC)"
    )


def downgrade() -> None:
    op.drop_index(
        "ix_image_record_earliest_post_date", table_name="image_record"
    )
    op.drop_column("image_record", "earliest_post_date")
