"""
Per-agent URL routes, generated from core/agent_catalog.py at import time.

    POST /document_identifier
    POST /parser_document
    POST /report_analyzer
    ...

Every route runs the same pipeline as POST /api/ai/execute — the only
difference is that the initiative comes from the URL instead of core/intent.py.

Set AGENT_ROUTE_PREFIX (e.g. "/agent") to mount them under a prefix instead
of at the root.
"""
import logging

from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from starlette.concurrency import run_in_threadpool

from enterprise_ai.agents.catalog import AGENT_ROUTES, AgentRoute, route_prefix
from enterprise_ai.core.auth import get_current_user
from enterprise_ai.core.database import get_db
from enterprise_ai.core.execution import parse_agent_form, run_execution
from enterprise_ai.schemas.executes import ExecuteResponse

logger = logging.getLogger(__name__)

# Root-level paths an agent slug must never shadow.
RESERVED_SLUGS = {
    "health", "docs", "redoc", "openapi.json", "api", "agents", "static", "favicon.ico",
}

router = APIRouter(prefix=route_prefix(), tags=["Agents"])


def _multipart_schema(entry: AgentRoute) -> dict:
    """Show only the form fields this agent actually accepts in /docs."""
    properties: dict = {}

    if entry.accepts_text:
        properties["input"] = {
            "type": "string",
            "description": "Text or JSON input",
        }

    if entry.uses_session:
        properties["session_id"] = {
            "type": "string",
            "description": "Session ID for multi-turn calls",
        }

    if len(entry.all_capabilities) > 1:
        properties["capability"] = {
            "type": "string",
            "enum": list(entry.all_capabilities),
            "description": f"Defaults to {entry.default_capability}",
        }

    if entry.uses_login:
        properties["login"] = {
            "type": "string",
            "description": "GoodBooks Login JSON, forwarded as the Login header",
        }

    if entry.accepts_file:
        properties["file"] = {
            "type": "string",
            "format": "binary",
            "description": "File upload" + (" (required)" if entry.requires_file else ""),
        }

    schema: dict = {"type": "object", "properties": properties}
    if entry.requires_file:
        schema["required"] = ["file"]

    return {
        "requestBody": {
            "content": {"multipart/form-data": {"schema": schema}},
            "required": entry.requires_file,
        }
    }


def _resolve_capability(entry: AgentRoute, parsed) -> str:
    """Explicit capability field wins, then the entry's rule, then the default."""
    if parsed.capability:
        if parsed.capability not in entry.all_capabilities:
            raise HTTPException(
                status_code=400,
                detail=(
                    f"Unsupported capability '{parsed.capability}' for {entry.slug}. "
                    f"Allowed: {', '.join(entry.all_capabilities)}"
                ),
            )
        return parsed.capability

    if entry.select_capability:
        return entry.select_capability(parsed.input_data, parsed.file, parsed.session_id)

    return entry.default_capability


def _make_endpoint(entry: AgentRoute):
    async def endpoint(
        request: Request,
        db: Session = Depends(get_db),
        current_user: dict = Depends(get_current_user),
    ):
        tenant_id = current_user["tenantId"]
        user_id = current_user["userId"]

        parsed = await parse_agent_form(request, tenant_id, user_id, hint=entry.slug)

        if entry.requires_file and parsed.file is None:
            raise HTTPException(
                status_code=400, detail=f"{entry.slug} requires a file upload"
            )

        if parsed.file is not None and not entry.accepts_file:
            raise HTTPException(
                status_code=400, detail=f"{entry.slug} does not accept file uploads"
            )

        if parsed.file is None and not entry.accepts_text:
            raise HTTPException(
                status_code=400, detail=f"{entry.slug} only accepts file uploads"
            )

        capability_code = _resolve_capability(entry, parsed)

        # run_execution is blocking (requests + SQLAlchemy) and a single agent
        # call can run for minutes. Off the event loop, so one slow agent cannot
        # stall /health and every other route for its full duration.
        execution, result = await run_in_threadpool(
            run_execution,
            db,
            tenant_id,
            user_id,
            entry.initiative_code,
            capability_code,
            parsed.input_data,
            parsed.file,
        )

        return ExecuteResponse(executionId=execution.AIexecutionid, result=result)

    endpoint.__name__ = f"execute_{entry.slug}"
    return endpoint


def _register_routes():
    for entry in AGENT_ROUTES:
        handler = _make_endpoint(entry)

        for index, slug in enumerate(entry.all_slugs):
            if not route_prefix() and slug in RESERVED_SLUGS:
                raise ValueError(
                    f"Agent slug '{slug}' collides with a reserved platform route. "
                    f"Rename it in core/agent_catalog.py or set AGENT_ROUTE_PREFIX."
                )

            router.add_api_route(
                f"/{slug}",
                handler,
                methods=["POST"],
                response_model=ExecuteResponse,
                summary=entry.summary,
                description=(
                    f"Initiative: `{entry.initiative_code}` · "
                    f"Capabilities: {', '.join(entry.all_capabilities)}"
                ),
                name=f"agent_{slug}",
                # Aliases stay functional but do not duplicate the Swagger entry.
                include_in_schema=(index == 0),
                openapi_extra=_multipart_schema(entry),
            )

    logger.info(
        "[agents] Registered %d agent routes under '%s'",
        sum(len(e.all_slugs) for e in AGENT_ROUTES),
        route_prefix() or "/",
    )


_register_routes()
