"""Add tool registry tables (tools, tool_resource_bindings, validation_attachments).

This migration creates the three core tool registry tables needed for
the Tool Registry Persistence layer:

- ``tools``: Registered tool and validation definitions with namespaced
  names as primary keys, JSON schema for inputs/outputs, capability
  metadata, and source-specific fields.
- ``tool_resource_bindings``: Declared resource bindings per tool,
  specifying typed resource slots with binding mode and access level.
- ``validation_attachments``: Runtime attachments linking validations to
  resources with optional project/plan scoping and ULID primary keys.

Revision ID: c1_001_tool_registry
Revises: b1_001_resource_registry
Create Date: 2026-02-14 12:00:00

"""

from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op

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


def upgrade() -> None:
    """Create tools, tool_resource_bindings, and validation_attachments tables."""
    # --- tools table ---
    op.create_table(
        "tools",
        # PK: namespaced tool name (e.g. "local/lint-check")
        sa.Column("name", sa.String(255), nullable=False),
        # Extracted namespace for filtering
        sa.Column("namespace", sa.String(100), nullable=False),
        # Extracted short name
        sa.Column("short_name", sa.String(150), nullable=False),
        # Human-readable description
        sa.Column("description", sa.Text(), nullable=False),
        # Discriminator: tool or validation
        sa.Column("tool_type", sa.String(20), nullable=False),
        # Source of implementation
        sa.Column("source", sa.String(20), nullable=False),
        # JSON Schema for inputs and outputs
        sa.Column("input_schema_json", sa.Text(), nullable=True),
        sa.Column("output_schema_json", sa.Text(), nullable=True),
        # Capability metadata (JSON blob)
        sa.Column("capability_json", sa.Text(), nullable=True),
        # Lifecycle hooks (JSON blob)
        sa.Column("lifecycle_json", sa.Text(), nullable=True),
        # Inline code for source=custom
        sa.Column("code", sa.Text(), nullable=True),
        # MCP fields
        sa.Column("mcp_server", sa.String(255), nullable=True),
        sa.Column("mcp_tool_name", sa.String(255), nullable=True),
        # Agent skill path
        sa.Column("agent_skill_path", sa.String(1024), nullable=True),
        # Execution timeout
        sa.Column(
            "timeout",
            sa.Integer(),
            nullable=False,
            server_default=sa.text("300"),
        ),
        # Validation wraps reference (FK to tools.name)
        sa.Column(
            "wraps",
            sa.String(255),
            sa.ForeignKey(
                "tools.name",
                ondelete="SET NULL",
                name="fk_tools_wraps",
            ),
            nullable=True,
        ),
        # Transform code for wrapped validations
        sa.Column("transform", sa.Text(), nullable=True),
        # Validation mode (required/informational)
        sa.Column("mode", sa.String(20), nullable=True),
        # Argument mapping JSON for wrapped validations
        sa.Column("argument_mapping_json", sa.Text(), nullable=True),
        # Timestamps (ISO-8601 strings)
        sa.Column("created_at", sa.String(30), nullable=False),
        sa.Column("updated_at", sa.String(30), nullable=False),
        # Constraints
        sa.PrimaryKeyConstraint("name"),
        sa.CheckConstraint(
            "tool_type IN ('tool', 'validation')",
            name="ck_tools_type",
        ),
        sa.CheckConstraint(
            "source IN ('mcp', 'agent_skill', 'builtin', 'custom', 'wrapped')",
            name="ck_tools_source",
        ),
    )

    op.create_index(
        "ix_tools_namespace",
        "tools",
        ["namespace"],
        unique=False,
    )
    op.create_index(
        "ix_tools_tool_type",
        "tools",
        ["tool_type"],
        unique=False,
    )
    op.create_index(
        "ix_tools_source",
        "tools",
        ["source"],
        unique=False,
    )

    # --- tool_resource_bindings table ---
    op.create_table(
        "tool_resource_bindings",
        sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
        sa.Column(
            "tool_name",
            sa.String(255),
            sa.ForeignKey(
                "tools.name",
                ondelete="CASCADE",
                name="fk_tool_resource_bindings_tool",
            ),
            nullable=False,
        ),
        sa.Column("slot_name", sa.String(100), nullable=False),
        sa.Column("resource_type", sa.String(255), nullable=False),
        sa.Column("access_mode", sa.String(20), nullable=False),
        sa.Column("binding_mode", sa.String(20), nullable=False),
        sa.Column("static_resource", sa.String(255), nullable=True),
        sa.Column(
            "required",
            sa.Boolean(),
            nullable=False,
            server_default=sa.text("1"),
        ),
        sa.Column("description", sa.Text(), nullable=True),
        sa.Column("created_at", sa.String(30), nullable=False),
        # Constraints
        sa.PrimaryKeyConstraint("id"),
    )

    op.create_index(
        "ix_tool_resource_bindings_tool_name",
        "tool_resource_bindings",
        ["tool_name"],
        unique=False,
    )

    # --- validation_attachments table ---
    op.create_table(
        "validation_attachments",
        # PK: ULID (26-char string)
        sa.Column("attachment_id", sa.String(26), nullable=False),
        # FK to tools.name (must be a validation)
        sa.Column(
            "validation_name",
            sa.String(255),
            sa.ForeignKey(
                "tools.name",
                ondelete="CASCADE",
                name="fk_validation_attachments_tool",
            ),
            nullable=False,
        ),
        # Resource reference (string identifier)
        sa.Column("resource_id", sa.String(255), nullable=False),
        # Mode: required or informational
        sa.Column("mode", sa.String(20), nullable=False),
        # Scoping fields
        sa.Column("project_name", sa.String(255), nullable=True),
        sa.Column("plan_id", sa.String(26), nullable=True),
        # Optional arguments for the validation invocation
        sa.Column("args_json", sa.Text(), nullable=True),
        # Timestamp
        sa.Column("created_at", sa.String(30), nullable=False),
        # Constraints
        sa.PrimaryKeyConstraint("attachment_id"),
        sa.CheckConstraint(
            "mode IN ('required', 'informational')",
            name="ck_validation_attachments_mode",
        ),
    )

    op.create_index(
        "ix_validation_attachments_validation_name",
        "validation_attachments",
        ["validation_name"],
        unique=False,
    )
    op.create_index(
        "ix_validation_attachments_resource_id",
        "validation_attachments",
        ["resource_id"],
        unique=False,
    )
    op.create_index(
        "ix_validation_attachments_project_name",
        "validation_attachments",
        ["project_name"],
        unique=False,
    )


def downgrade() -> None:
    """Drop tool registry tables."""
    # Drop in reverse FK order
    op.drop_index(
        "ix_validation_attachments_project_name",
        table_name="validation_attachments",
    )
    op.drop_index(
        "ix_validation_attachments_resource_id",
        table_name="validation_attachments",
    )
    op.drop_index(
        "ix_validation_attachments_validation_name",
        table_name="validation_attachments",
    )
    op.drop_table("validation_attachments")

    op.drop_index(
        "ix_tool_resource_bindings_tool_name",
        table_name="tool_resource_bindings",
    )
    op.drop_table("tool_resource_bindings")

    op.drop_index("ix_tools_source", table_name="tools")
    op.drop_index("ix_tools_tool_type", table_name="tools")
    op.drop_index("ix_tools_namespace", table_name="tools")
    op.drop_table("tools")
