"""Add lifecycle_plans table for v3 plan lifecycle.

This migration creates the lifecycle_plans table that tracks plans through
the four-phase lifecycle: Action -> Strategize -> Execute -> Apply -> Applied.
Plans use ULID text primary keys and support hierarchical parent/child relationships.

Depends on: a5_001_actions_v3 (actions_v3 table must exist for foreign key)

Revision ID: a5_002_lifecycle_plans
Revises: a5_001_actions_v3
Create Date: 2026-02-09 00:00:00

"""

from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op

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


def upgrade() -> None:
    """Create lifecycle_plans table for v3 plan lifecycle tracking."""
    op.create_table(
        "lifecycle_plans",
        # Identity - ULID primary key (26 chars, Crockford base32)
        sa.Column("plan_id", sa.String(26), nullable=False),
        # Hierarchy - parent/root for subplan trees
        sa.Column(
            "parent_plan_id",
            sa.String(26),
            sa.ForeignKey(
                "lifecycle_plans.plan_id",
                ondelete="SET NULL",
                name="fk_lifecycle_plans_parent",
            ),
            nullable=True,
        ),
        sa.Column(
            "root_plan_id",
            sa.String(26),
            sa.ForeignKey(
                "lifecycle_plans.plan_id",
                ondelete="SET NULL",
                name="fk_lifecycle_plans_root",
            ),
            nullable=True,
        ),
        # Action reference - the template that created this plan
        sa.Column(
            "action_id",
            sa.String(26),
            sa.ForeignKey(
                "actions_v3.action_id",
                ondelete="RESTRICT",
                name="fk_lifecycle_plans_action",
            ),
            nullable=False,
        ),
        # Lifecycle phase and state
        sa.Column("phase", sa.String(20), nullable=False),
        sa.Column("state", sa.String(20), nullable=False),
        # Attempt counter - increments on re-execution after correction
        sa.Column(
            "attempt",
            sa.Integer(),
            nullable=False,
            server_default=sa.text("1"),
        ),
        # Automation level
        sa.Column(
            "automation_level",
            sa.String(30),
            nullable=False,
            server_default="manual",
        ),
        # Naming (namespaced name stored as components)
        sa.Column("name", sa.String(255), nullable=False),
        sa.Column("namespace", sa.String(100), nullable=False),
        sa.Column("short_name", sa.String(150), nullable=False),
        # Description and completion criteria
        sa.Column("description", sa.Text(), nullable=False),
        sa.Column("definition_of_done", sa.Text(), nullable=True),
        # Actor references
        sa.Column("strategy_actor", sa.String(255), nullable=True),
        sa.Column("execution_actor", sa.String(255), nullable=True),
        # Project references - JSON array of project ULIDs
        sa.Column("project_ids", sa.Text(), nullable=False, server_default="[]"),
        # Arguments - JSON object mapping argument name to provided value
        sa.Column("arguments", sa.Text(), nullable=True),
        # Strategy context - JSON blob storing Strategize phase outputs
        sa.Column("strategy_context", sa.Text(), nullable=True),
        # Execution log - JSON array of execution events
        sa.Column("execution_log", sa.Text(), nullable=True),
        # ChangeSet reference
        sa.Column("changeset_id", sa.String(26), nullable=True),
        # Sandbox references - JSON object mapping resource_id to sandbox_path
        sa.Column("sandbox_refs", sa.Text(), nullable=True),
        # Error tracking
        sa.Column("error_message", sa.Text(), nullable=True),
        sa.Column("error_details", sa.Text(), nullable=True),
        # Metadata
        sa.Column("created_by", sa.String(255), nullable=True),
        sa.Column("tags", sa.Text(), nullable=False, server_default="[]"),
        sa.Column(
            "reusable",
            sa.Boolean(),
            nullable=False,
            server_default=sa.text("1"),
        ),
        sa.Column(
            "read_only",
            sa.Boolean(),
            nullable=False,
            server_default=sa.text("0"),
        ),
        # Timestamps (ISO8601 strings)
        sa.Column("created_at", sa.String(30), nullable=False),
        sa.Column("updated_at", sa.String(30), nullable=False),
        sa.Column("strategize_started_at", sa.String(30), nullable=True),
        sa.Column("strategize_completed_at", sa.String(30), nullable=True),
        sa.Column("execute_started_at", sa.String(30), nullable=True),
        sa.Column("execute_completed_at", sa.String(30), nullable=True),
        sa.Column("apply_started_at", sa.String(30), nullable=True),
        sa.Column("applied_at", sa.String(30), nullable=True),
        # Constraints
        sa.PrimaryKeyConstraint("plan_id"),
        sa.CheckConstraint(
            "phase IN ('action', 'strategize', 'execute', 'apply', 'applied')",
            name="ck_lifecycle_plans_phase",
        ),
        sa.CheckConstraint(
            "state IN ('available', 'draft', 'archived', "
            "'queued', 'processing', 'errored', 'complete', 'cancelled')",
            name="ck_lifecycle_plans_state",
        ),
        sa.CheckConstraint(
            "automation_level IN ('manual', 'review_before_apply', 'full_automation')",
            name="ck_lifecycle_plans_automation",
        ),
    )

    # Indices for common queries
    op.create_index(
        "ix_lifecycle_plans_phase",
        "lifecycle_plans",
        ["phase"],
        unique=False,
    )
    op.create_index(
        "ix_lifecycle_plans_state",
        "lifecycle_plans",
        ["state"],
        unique=False,
    )
    op.create_index(
        "ix_lifecycle_plans_parent",
        "lifecycle_plans",
        ["parent_plan_id"],
        unique=False,
    )
    op.create_index(
        "ix_lifecycle_plans_root",
        "lifecycle_plans",
        ["root_plan_id"],
        unique=False,
    )
    op.create_index(
        "ix_lifecycle_plans_created",
        "lifecycle_plans",
        ["created_at"],
        unique=False,
    )
    op.create_index(
        "ix_lifecycle_plans_action",
        "lifecycle_plans",
        ["action_id"],
        unique=False,
    )


def downgrade() -> None:
    """Drop lifecycle_plans table."""
    op.drop_index("ix_lifecycle_plans_action", table_name="lifecycle_plans")
    op.drop_index("ix_lifecycle_plans_created", table_name="lifecycle_plans")
    op.drop_index("ix_lifecycle_plans_root", table_name="lifecycle_plans")
    op.drop_index("ix_lifecycle_plans_parent", table_name="lifecycle_plans")
    op.drop_index("ix_lifecycle_plans_state", table_name="lifecycle_plans")
    op.drop_index("ix_lifecycle_plans_phase", table_name="lifecycle_plans")
    op.drop_table("lifecycle_plans")
