"""image_asset disposition/attempt_index — true-intermediate capture 메타 (persist-all Wave2).

중간 생성물(거부 후보/재시도/진단 아티팩트)을 image_asset 으로 영속화할 때,
그 산출물의 **생애 결과(disposition)** 와 **재시도 회차(attempt_index)** 를 구분하기
위한 nullable add-column 마이그레이션.

- disposition: ``accepted | rejected | diagnostic | cache_hit_source`` (자유 Text, 코드 enum).
  최종 채택(accepted)·downstream 거부(rejected)·검증용 진단 아티팩트(diagnostic)·
  캐시 적중 원본(cache_hit_source) 구분. NULL=미상(기존 행/legacy).
- attempt_index: 같은 논리 생성의 재시도 회차(0-base). candidate_index(같은 호출
  내 후보 번호)와 직교 — retry loop 회차를 별도로 기록.
- 인덱스 1개: (project_id, episode_id, disposition) — 캔버스 거부본 필터용.
- ★ 008 패턴 정합: idempotent(inspector skip + if_not_exists), 최종물 행에는 NULL.
- downgrade = drop (인덱스 → 컬럼 순).

Revision ID: 009_image_asset_disposition
Revises: 008_image_asset_gen_cols
Create Date: 2026-06-30
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


revision: str = "009_image_asset_disposition"
down_revision: Union[str, None] = "008_image_asset_gen_cols"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


_TABLE = "image_asset"

_NEW_COLUMNS = (
    ("disposition", lambda: sa.Column("disposition", sa.Text(), nullable=True)),
    ("attempt_index", lambda: sa.Column("attempt_index", sa.Integer(), nullable=True)),
)

_NEW_INDEXES = (
    ("idx_image_asset_disposition", ["project_id", "episode_id", "disposition"]),
)


def upgrade() -> None:
    bind = op.get_bind()
    inspector = sa.inspect(bind)

    existing_cols = {col["name"] for col in inspector.get_columns(_TABLE)}
    for name, factory in _NEW_COLUMNS:
        if name not in existing_cols:
            op.add_column(_TABLE, factory())

    existing_idx = {ix["name"] for ix in inspector.get_indexes(_TABLE)}
    for idx_name, cols in _NEW_INDEXES:
        if idx_name not in existing_idx:
            op.create_index(idx_name, _TABLE, cols, if_not_exists=True)


def downgrade() -> None:
    bind = op.get_bind()
    inspector = sa.inspect(bind)

    existing_idx = {ix["name"] for ix in inspector.get_indexes(_TABLE)}
    for idx_name, _cols in _NEW_INDEXES:
        if idx_name in existing_idx:
            op.drop_index(idx_name, table_name=_TABLE)

    existing_cols = {col["name"] for col in inspector.get_columns(_TABLE)}
    for name, _factory in reversed(_NEW_COLUMNS):
        if name in existing_cols:
            op.drop_column(_TABLE, name)
