"""record a synced-state fingerprint per playlist

Revision ID: 0013_playlist_fingerprint
Revises: 0012_watch_later
Create Date: 2026-06-15

Adds playlists.synced_fingerprint — a SHA-256 of the playlist's name + ordered video ids
as last synced from / pushed to YouTube. `dirty` is then derived by comparing the current
fingerprint to this baseline, so manually restoring the original order clears dirty without
a YouTube read. Backfills the baseline for already-clean linked playlists (the current
state == the synced state for them); dirty ones are left NULL so they stay dirty until the
next real sync establishes a baseline.
"""
import hashlib
from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

revision: str = "0013_playlist_fingerprint"
down_revision: Union[str, None] = "0012_watch_later"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def _fingerprint(name: str, ids: list[str]) -> str:
    h = hashlib.sha256()
    h.update((name or "").encode())
    h.update(b"\n")
    h.update(",".join(ids).encode())
    return h.hexdigest()


def upgrade() -> None:
    op.add_column(
        "playlists", sa.Column("synced_fingerprint", sa.String(), nullable=True)
    )
    bind = op.get_bind()
    # Backfill clean, YouTube-linked playlists: their current state IS the synced state.
    rows = bind.execute(
        sa.text(
            "SELECT id, name FROM playlists "
            "WHERE yt_playlist_id IS NOT NULL AND dirty = false"
        )
    ).fetchall()
    for pid, name in rows:
        ids = [
            r[0]
            for r in bind.execute(
                sa.text(
                    "SELECT video_id FROM playlist_items "
                    "WHERE playlist_id = :pid ORDER BY position"
                ),
                {"pid": pid},
            ).fetchall()
        ]
        bind.execute(
            sa.text("UPDATE playlists SET synced_fingerprint = :fp WHERE id = :pid"),
            {"fp": _fingerprint(name, ids), "pid": pid},
        )


def downgrade() -> None:
    op.drop_column("playlists", "synced_fingerprint")
