"""Seed built-in tool types into the database."""

import logging

from sqlalchemy import select, text

from src.database import SessionLocal
from src.models import ToolDefinitionManifest, ToolType

logger = logging.getLogger(__name__)

# All supported built-in tools run their long-lived process with these IDs.
# Canonical writable Config Profile mounts can therefore be shared without
# per-instance ownership changes.
BUILTIN_USER_UID = 1000
BUILTIN_USER_GID = 1000

BUILTIN_TOOL_TYPES = [
    {
        "name": "code-server",
        "display_name": "VS Code Server",
        "description": "VS Code running in the browser via code-server",
        "category": "editor",
        "interface_type": "web",
        "compose_template": """version: "3.8"
services:
  code-server:
    image: lscr.io/linuxserver/code-server:latest
    container_name: {{TOOL_NAME}}
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/London
    volumes:
      - {{REPO_PATH}}:/config/workspace
    ports:
      - "8443:8443"
    restart: 'no'""",
        "default_port": 8443,
        "required_variables": ["REPO_PATH", "TOOL_NAME"],
    },
    {
        "name": "jupyter-notebook",
        "display_name": "Jupyter Notebook",
        "description": "Jupyter Lab for interactive development",
        "category": "notebook",
        "interface_type": "web",
        "default_port": 8888,
        "compose_template": """version: "3.8"
services:
  jupyter:
    image: jupyter/scipy-notebook:latest
    container_name: {{TOOL_NAME}}
    environment:
      - JUPYTER_ENABLE_LAB=yes
      - NB_UID=1000
      - NB_GID=1000
    volumes:
      - {{REPO_PATH}}:/home/jovyan/work
    ports:
      - "8888:8888"
    restart: 'no'""",
        "required_variables": ["REPO_PATH", "TOOL_NAME"],
    },
    {
        "name": "opencode",
        "display_name": "OpenCode",
        "description": "AI coding assistant - run opencode in terminal",
        "category": "ai-assistant",
        "interface_type": "terminal",
        "default_port": 3000,
        "compose_template": """version: "3.8"
services:
  opencode:
    image: node:20-slim
    container_name: {{TOOL_NAME}}
    working_dir: /home/node/{{WORKSPACE_NAME}}
    volumes:
      - {{REPO_PATH}}:/home/node/{{WORKSPACE_NAME}}
    ports:
      - "3000:3000"
    command: >
      sh -ec "apt-get update && apt-get install -y git ca-certificates &&
              npm install -g opencode-ai &&
              exec setpriv --reuid=node --regid=node --init-groups opencode server"
    stdin_open: true
    tty: true
    restart: 'no'""",
        "required_variables": ["REPO_PATH", "TOOL_NAME"],
    },
]


def _standardize_builtin_manifest_user(manifest: dict) -> bool:
    """Set the built-in manifest user to the shared UID/GID in place.

    The helper deliberately recognizes only Headquarter's conventional
    ``user`` account so it cannot rewrite a future custom tool definition.
    """
    user = manifest.get("user")
    if not isinstance(user, dict) or user.get("name") != "user":
        return False

    changed = user.get("uid") != BUILTIN_USER_UID or user.get("gid") != BUILTIN_USER_GID
    if changed:
        user["uid"] = BUILTIN_USER_UID
        user["gid"] = BUILTIN_USER_GID
    return changed


async def _standardize_pi_agent_manifest(session) -> None:
    """Bring the built-in Pi Agent manifest in line with shared mount IDs."""
    manifest_definition = await session.scalar(
        select(ToolDefinitionManifest).where(
            ToolDefinitionManifest.name == "pi-agent",
            ToolDefinitionManifest.created_by_id.is_(None),
        )
    )
    if manifest_definition is None:
        return

    manifest = dict(manifest_definition.manifest)
    if _standardize_builtin_manifest_user(manifest):
        manifest_definition.manifest = manifest
        logger.info("Standardized built-in Pi Agent user to 1000:1000")


async def _table_exists(session, table_name: str) -> bool:
    """Check if a table exists in the database."""
    try:
        result = await session.execute(
            text(
                """
                SELECT EXISTS (
                    SELECT FROM information_schema.tables
                    WHERE table_schema = 'public'
                    AND table_name = :table_name
                )
            """
            ),
            {"table_name": table_name},
        )
        return result.scalar() or False
    except Exception:
        return False


async def seed_builtin_tool_types():
    """Create or update built-in tool types in the database.

    Built-in tool types have no creator (created_by_id=None) and provide
    out-of-the-box tools for users without requiring manual tool creation.
    """
    async with SessionLocal() as session:
        # Check if tool_types table exists before attempting to seed
        if not await _table_exists(session, "tool_types"):
            logger.warning(
                "tool_types table does not exist. Skipping seeding. "
                "Migrations may not have run yet."
            )
            return

        await _standardize_pi_agent_manifest(session)

        for tool_data in BUILTIN_TOOL_TYPES:
            existing = await session.scalar(
                select(ToolType).where(ToolType.name == tool_data["name"])
            )
            if not existing:
                tool_type = ToolType(
                    name=tool_data["name"],
                    display_name=tool_data["display_name"],
                    description=tool_data["description"],
                    category=tool_data["category"],
                    interface_type=tool_data["interface_type"],
                    definition_type="compose",
                    compose_template=tool_data["compose_template"],
                    required_variables=tool_data["required_variables"],
                    default_port=tool_data.get("default_port", 0),
                    created_by_id=None,
                )
                session.add(tool_type)
                logger.info("Created built-in tool type: %s", tool_data["name"])
            else:
                # Update existing built-in tool types to reflect code changes
                existing.display_name = tool_data["display_name"]
                existing.description = tool_data["description"]
                existing.category = tool_data["category"]
                existing.interface_type = tool_data["interface_type"]
                existing.definition_type = "compose"
                existing.compose_template = tool_data["compose_template"]
                existing.required_variables = tool_data["required_variables"]
                existing.default_port = tool_data.get("default_port", 0)
                logger.info("Updated built-in tool type: %s", tool_data["name"])

        await session.commit()
        logger.info("Built-in tool types seeded successfully.")
