"""Make built-in actors virtual: drop is_built_in column, add actor_preferences table.

Removes the ``is_built_in`` column from the ``actors`` table (built-in actors are
now resolved on-demand from the provider registry and are never persisted).

Adds a new ``actor_preferences`` singleton table that stores a single
``default_actor_name`` string, allowing ``set-default`` to persist just the actor
name even when the named actor is a virtual built-in (not in the DB).

Revision ID: m10_001_virtual_builtin_actors
Revises: a5_006_action_invariants_unique_constraint
Create Date: 2026-04-29
"""

from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "m10_001_virtual_builtin_actors"
down_revision: str | None = "a5_006_action_invariants_unique_constraint"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
    """Drop is_built_in from actors; add actor_preferences singleton table."""

    # -- drop is_built_in column from actors --------------------------------
    # SQLite does not support DROP COLUMN directly before 3.35.  We use
    # batch mode (recreate) to be safe across all supported SQLite versions.
    with op.batch_alter_table("actors") as batch_op:
        batch_op.drop_column("is_built_in")

    # -- actor_preferences: singleton row for global actor settings ---------
    op.create_table(
        "actor_preferences",
        sa.Column(
            "id",
            sa.Integer,
            primary_key=True,
            autoincrement=False,
            nullable=False,
        ),
        sa.Column("default_actor_name", sa.String(255), nullable=True),
        sa.PrimaryKeyConstraint("id"),
    )


def downgrade() -> None:
    """Reverse: drop actor_preferences table; restore is_built_in column.

    Before dropping ``actor_preferences``, the stored ``default_actor_name``
    is migrated back to the ``is_default=True`` flag on the corresponding
    actor row so that the legacy schema can still identify the default actor.
    """
    # Migrate default_actor_name preference back to is_default flag.
    conn = op.get_bind()
    row = conn.execute(
        sa.text("SELECT default_actor_name FROM actor_preferences WHERE id = 1")
    ).fetchone()
    if row is not None and row[0]:
        default_name: str = row[0]
        # Clear all is_default flags first, then set the stored default.
        conn.execute(sa.text("UPDATE actors SET is_default = 0"))
        conn.execute(
            sa.text("UPDATE actors SET is_default = 1 WHERE name = :name"),
            {"name": default_name},
        )

    op.drop_table("actor_preferences")

    with op.batch_alter_table("actors") as batch_op:
        batch_op.add_column(
            sa.Column(
                "is_built_in",
                sa.Boolean,
                nullable=False,
                server_default=sa.text("0"),
            )
        )
