"""add tool definition manifests

Revision ID: 2026_05_28_add_tool_definition_manifests
Revises: 20260527_160017_add_pi_agent
Create Date: 2026-05-28T11:00:00

"""

import json
import uuid
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision: str = "2026_05_28_add_tool_definition_manifests"
down_revision: Union[str, None] = "20260527_160017_add_pi_agent"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

BASE_UBUNTU_ID = uuid.UUID("a1b2c3d4-e5f6-7890-abcd-ef1234567890")
PI_AGENT_MANIFEST_ID = uuid.UUID("d07b8376-2151-4119-8c1d-27f792aae9a3")


def upgrade() -> None:
    conn = op.get_bind()

    # ── Create tool_definition_manifests table ───────────────────────
    op.create_table(
        "tool_definition_manifests",
        sa.Column("id", sa.UUID(), nullable=False),
        sa.Column("name", sa.String(64), nullable=False),
        sa.Column("display_name", sa.String(128), nullable=False),
        sa.Column("description", sa.Text(), nullable=True),
        sa.Column("category", sa.String(64), nullable=True),
        sa.Column("interface_type", sa.String(16), nullable=False),
        sa.Column("base_image", sa.String(256), nullable=True),
        sa.Column("base_definition_id", sa.UUID(), nullable=True),
        sa.Column(
            "base_version", sa.String(32), nullable=False, server_default="latest"
        ),
        sa.Column("manifest", sa.JSON(), nullable=False),
        sa.Column("dockerfile_cache", sa.Text(), nullable=True),
        sa.Column("compose_cache", sa.Text(), nullable=True),
        sa.Column("version", sa.String(32), nullable=False, server_default="v1"),
        sa.Column("is_base", sa.Boolean(), nullable=False, server_default="false"),
        sa.Column("created_by_id", sa.UUID(), nullable=True),
        sa.Column(
            "created_at", sa.TIMESTAMP(timezone=True), server_default=sa.func.now()
        ),
        sa.Column(
            "updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.func.now()
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("name"),
        sa.ForeignKeyConstraint(
            ["base_definition_id"], ["tool_definition_manifests.id"]
        ),
        sa.ForeignKeyConstraint(["created_by_id"], ["users.id"]),
        sa.CheckConstraint(
            "(base_image IS NOT NULL) OR (base_definition_id IS NOT NULL)",
            name="ck_tool_definition_manifests_base_required",
        ),
    )

    # ── Add columns to tool_types ────────────────────────────────────
    # Check if manifest_id exists before adding
    conn = op.get_bind()
    result = conn.execute(
        sa.text("""
            SELECT column_name FROM information_schema.columns
            WHERE table_name = 'tool_types' AND column_name = 'manifest_id'
        """)
    )
    if not result.fetchone():
        op.add_column("tool_types", sa.Column("manifest_id", sa.UUID(), nullable=True))
        op.create_foreign_key(
            "fk_tool_types_manifest_id",
            "tool_types",
            "tool_definition_manifests",
            ["manifest_id"],
            ["id"],
        )

    # Update definition_type to allow 'legacy' and 'manifest'
    result = conn.execute(
        sa.text("""
            SELECT constraint_name FROM information_schema.check_constraints
            WHERE constraint_name = 'chk_definition_type'
        """)
    )
    if result.fetchone():
        op.drop_constraint("chk_definition_type", "tool_types", type_="check")

    op.execute("ALTER TABLE tool_types ALTER COLUMN definition_type TYPE VARCHAR(16)")
    op.execute(
        "ALTER TABLE tool_types ALTER COLUMN definition_type SET DEFAULT 'legacy'"
    )

    # ── Add columns to tool_instances ────────────────────────────────
    result = conn.execute(
        sa.text("""
            SELECT column_name FROM information_schema.columns
            WHERE table_name = 'tool_instances' AND column_name = 'manifest_compiled_at'
        """)
    )
    if not result.fetchone():
        op.add_column(
            "tool_instances",
            sa.Column(
                "manifest_compiled_at", sa.TIMESTAMP(timezone=True), nullable=True
            ),
        )

    result = conn.execute(
        sa.text("""
            SELECT column_name FROM information_schema.columns
            WHERE table_name = 'tool_instances' AND column_name = 'image_tag'
        """)
    )
    if not result.fetchone():
        op.add_column(
            "tool_instances",
            sa.Column("image_tag", sa.String(256), nullable=True),
        )

    # ── Data migration: create base definition + pi-agent manifest ───
    conn.execute(
        sa.text(
            """
            INSERT INTO tool_definition_manifests
            (id, name, display_name, description, interface_type, base_image,
             manifest, is_base, version, created_at, updated_at)
            VALUES
            (:base_id, 'ubuntu-24.04-dev', 'Ubuntu 24.04 Dev Base',
             'Base development environment with build tools', 'terminal',
             'ubuntu:24.04', :base_manifest, true, 'v1', now(), now())
        """
        ),
        {
            "base_id": BASE_UBUNTU_ID,
            "base_manifest": json.dumps(
                {
                    "name": "ubuntu-24.04-dev",
                    "display_name": "Ubuntu 24.04 Dev Base",
                    "interface_type": "terminal",
                    "base_image": "ubuntu:24.04",
                    "packages": {
                        "apt": [
                            "curl",
                            "wget",
                            "git",
                            "build-essential",
                            "ca-certificates",
                            "python3",
                            "python3-pip",
                        ]
                    },
                    "user": {
                        "name": "user",
                        "uid": 1000,
                        "gid": 1000,
                        "create_home": True,
                        "shell": "/bin/bash",
                    },
                    "env": {"DEBIAN_FRONTEND": "noninteractive"},
                }
            ),
        },
    )

    conn.execute(
        sa.text(
            """
            INSERT INTO tool_definition_manifests
            (id, name, display_name, description, category, interface_type,
             base_definition_id, base_version, manifest, version, created_at, updated_at)
            VALUES
            (:manifest_id, 'pi-agent', 'Pi Agent',
             'Terminal-based coding harness with nvim, ranger, tmux',
             'development', 'terminal', :base_id, 'v1', :manifest, 'v1',
             now(), now())
        """
        ),
        {
            "manifest_id": PI_AGENT_MANIFEST_ID,
            "base_id": BASE_UBUNTU_ID,
            "manifest": json.dumps(
                {
                    "name": "pi-agent",
                    "display_name": "Pi Agent",
                    "description": "Terminal-based coding harness",
                    "category": "development",
                    "interface_type": "terminal",
                    "base_definition_id": str(BASE_UBUNTU_ID),
                    "base_version": "v1",
                    "packages": {
                        "apt": [
                            "neovim",
                            "ranger",
                            "tmux",
                            "htop",
                            "tree",
                            "jq",
                        ],
                        "node": {"version": "20"},
                        "npm_global": ["@earendil-works/pi-coding-agent"],
                    },
                    "user": {
                        "name": "user",
                        "uid": 1001,
                        "gid": 1001,
                        "create_home": True,
                        "shell": "/bin/bash",
                    },
                    "env": {"DEBIAN_FRONTEND": "noninteractive"},
                    "scripts": {
                        "build": [
                            "git config --global init.defaultBranch main && git config --global user.email 'dev@headquarter.local' && git config --global user.name 'Developer'",
                            "mkdir -p /home/user/.config/ranger && echo 'set preview_files true' > /home/user/.config/ranger/rc.conf",
                        ],
                        "startup": [
                            "if [ -d /workspace ]; then sudo chown -R user:user /workspace 2>/dev/null || true; fi",
                        ],
                    },
                    "mounts": [
                        {
                            "name": "workspace",
                            "target": "/workspace",
                            "source_type": "repo",
                            "writable": True,
                            "owner": "user",
                        },
                    ],
                    "runtime": {
                        "command": ["/bin/bash"],
                        "stdin_open": True,
                        "tty": True,
                        "working_dir": "/workspace",
                    },
                }
            ),
        },
    )

    # ── Update existing pi-agent tool_type ───────────────────────────
    conn.execute(
        sa.text(
            """
            UPDATE tool_types
            SET manifest_id = :manifest_id,
                definition_type = 'manifest',
                dockerfile_template = NULL,
                compose_template = NULL
            WHERE name = 'pi-agent'
        """
        ),
        {"manifest_id": PI_AGENT_MANIFEST_ID},
    )


def downgrade() -> None:
    conn = op.get_bind()

    # Restore pi-agent templates if manifest_id column exists
    result = conn.execute(
        sa.text("""
            SELECT column_name FROM information_schema.columns
            WHERE table_name = 'tool_types' AND column_name = 'manifest_id'
        """)
    )
    has_manifest_id = result.fetchone() is not None

    if has_manifest_id:
        conn.execute(
            sa.text(
                """
                UPDATE tool_types
                SET manifest_id = NULL,
                    definition_type = 'dockerfile',
                    dockerfile_template = :dockerfile,
                    compose_template = :compose
                WHERE name = 'pi-agent'
            """
            ),
            {
                "dockerfile": """# Pi Coding Agent - Terminal-based coding harness
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \\
    curl wget git neovim ranger tmux htop tree jq \\
    ca-certificates python3 python3-pip build-essential \\
    && rm -rf /var/lib/apt/lists/*

RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \\
    && apt-get install -y nodejs \\
    && rm -rf /var/lib/apt/lists/*

RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent

RUN useradd -m -s /bin/bash user
WORKDIR /home/user

RUN git config --global init.defaultBranch main \\
    && git config --global user.email "dev@headquarter.local" \\
    && git config --global user.name "Developer"

RUN printf '%s\\n' 'set -g mouse on' 'set -g default-terminal "screen-256color"' > /home/user/.tmux.conf

RUN mkdir -p /home/user/.config/ranger \\
    && printf '%s\\n' 'set preview_files true' 'set use_preview_script true' > /home/user/.config/ranger/rc.conf

RUN mkdir -p /home/user/.pi/agent

USER user

CMD ["/bin/bash"]
""",
                "compose": """services:
  app:
    build: .
    stdin_open: true
    tty: true
    volumes:
      - ${REPO_PATH}:/workspace
    working_dir: /workspace
    command: /bin/bash""",
            },
        )

    # Drop columns conditionally
    result = conn.execute(
        sa.text("""
            SELECT column_name FROM information_schema.columns
            WHERE table_name = 'tool_instances' AND column_name = 'image_tag'
        """)
    )
    if result.fetchone():
        op.drop_column("tool_instances", "image_tag")

    result = conn.execute(
        sa.text("""
            SELECT column_name FROM information_schema.columns
            WHERE table_name = 'tool_instances' AND column_name = 'manifest_compiled_at'
        """)
    )
    if result.fetchone():
        op.drop_column("tool_instances", "manifest_compiled_at")

    if has_manifest_id:
        op.drop_constraint(
            "fk_tool_types_manifest_id", "tool_types", type_="foreignkey"
        )
        op.drop_column("tool_types", "manifest_id")

    op.drop_table("tool_definition_manifests")
