"""Add decision tree tables.

Creates the ``decisions`` and ``decision_dependencies`` tables for
the decision tree subsystem (Stage M3.2).

Revision ID: m4_001_decision_tables
Revises: d0_002_merge_changeset_and_locks
Create Date: 2026-02-23
"""

from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op

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


def upgrade() -> None:
    """Create decisions and decision_dependencies tables."""

    # -- decisions ----------------------------------------------------------
    op.create_table(
        "decisions",
        sa.Column("decision_id", sa.String(26), nullable=False),
        sa.Column(
            "plan_id",
            sa.String(26),
            sa.ForeignKey("v3_plans.plan_id", ondelete="CASCADE"),
            nullable=False,
        ),
        sa.Column(
            "parent_decision_id",
            sa.String(26),
            sa.ForeignKey("decisions.decision_id", ondelete="SET NULL"),
            nullable=True,
        ),
        sa.Column("sequence_number", sa.Integer, nullable=False),
        sa.Column("decision_type", sa.String(30), nullable=False),
        sa.Column("question", sa.Text, nullable=False),
        sa.Column("chosen_option", sa.Text, nullable=False),
        sa.Column("alternatives_considered_json", sa.Text, nullable=True),
        sa.Column("confidence_score", sa.Float, nullable=True),
        sa.Column("context_snapshot_json", sa.Text, nullable=False),
        sa.Column("rationale", sa.Text, nullable=True),
        sa.Column("actor_reasoning", sa.Text, nullable=True),
        sa.Column("downstream_decision_ids_json", sa.Text, nullable=True),
        sa.Column("downstream_plan_ids_json", sa.Text, nullable=True),
        sa.Column("artifacts_produced_json", sa.Text, nullable=True),
        sa.Column("created_at", sa.String(30), nullable=False),
        sa.Column(
            "is_correction",
            sa.Boolean,
            nullable=False,
            server_default=sa.text("0"),
        ),
        sa.Column(
            "corrects_decision_id",
            sa.String(26),
            sa.ForeignKey("decisions.decision_id", ondelete="SET NULL"),
            nullable=True,
        ),
        sa.Column("correction_reason", sa.Text, nullable=True),
        sa.Column(
            "superseded_by",
            sa.String(26),
            sa.ForeignKey("decisions.decision_id", ondelete="SET NULL"),
            nullable=True,
        ),
        sa.PrimaryKeyConstraint("decision_id"),
        sa.CheckConstraint(
            "decision_type IN ("
            "'prompt_definition', 'invariant_enforced', "
            "'strategy_choice', 'implementation_choice', "
            "'resource_selection', 'subplan_spawn', "
            "'subplan_parallel_spawn', 'tool_invocation', "
            "'error_recovery', 'validation_response', "
            "'user_intervention')",
            name="ck_decisions_type",
        ),
        sa.CheckConstraint(
            "confidence_score IS NULL OR "
            "(confidence_score >= 0.0 AND confidence_score <= 1.0)",
            name="ck_decisions_confidence",
        ),
    )
    op.create_index("ix_decisions_plan", "decisions", ["plan_id"])
    op.create_index("ix_decisions_parent", "decisions", ["parent_decision_id"])
    op.create_index("ix_decisions_type", "decisions", ["decision_type"])
    op.create_index("ix_decisions_created", "decisions", ["created_at"])
    op.create_index(
        "ix_decisions_plan_seq", "decisions", ["plan_id", "sequence_number"]
    )

    # -- decision_dependencies ----------------------------------------------
    op.create_table(
        "decision_dependencies",
        sa.Column(
            "source_decision_id",
            sa.String(26),
            sa.ForeignKey("decisions.decision_id", ondelete="CASCADE"),
            nullable=False,
        ),
        sa.Column(
            "target_decision_id",
            sa.String(26),
            sa.ForeignKey("decisions.decision_id", ondelete="CASCADE"),
            nullable=False,
        ),
        sa.Column(
            "relationship_type",
            sa.String(30),
            nullable=False,
            server_default="influences",
        ),
        sa.Column("created_at", sa.String(30), nullable=False),
        sa.PrimaryKeyConstraint("source_decision_id", "target_decision_id"),
        sa.CheckConstraint(
            "source_decision_id != target_decision_id",
            name="ck_decision_deps_no_self_loop",
        ),
    )
    op.create_index(
        "ix_decision_deps_source",
        "decision_dependencies",
        ["source_decision_id"],
    )
    op.create_index(
        "ix_decision_deps_target",
        "decision_dependencies",
        ["target_decision_id"],
    )


def downgrade() -> None:
    """Drop decision_dependencies and decisions tables."""

    # Drop decision_dependencies first (FK to decisions)
    op.drop_index("ix_decision_deps_target", table_name="decision_dependencies")
    op.drop_index("ix_decision_deps_source", table_name="decision_dependencies")
    op.drop_table("decision_dependencies")

    # Drop decisions
    op.drop_index("ix_decisions_plan_seq", table_name="decisions")
    op.drop_index("ix_decisions_created", table_name="decisions")
    op.drop_index("ix_decisions_type", table_name="decisions")
    op.drop_index("ix_decisions_parent", table_name="decisions")
    op.drop_index("ix_decisions_plan", table_name="decisions")
    op.drop_table("decisions")
