"""API routes for the Agents plugin — /api/v1/agents.

Endpoints: agent definitions CRUD, execute, test-run, runs, versions, tools.
"""

from __future__ import annotations

import logging
import uuid
from typing import Any

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.db import get_db, set_tenant_context
from app.deps import get_current_user, require_permission
from app.plugins.builtins.automation.models import (
    AgentDefinition,
    AgentRun,
    AgentVersion,
)
from app.plugins.builtins.automation.schemas import (
    AgentDefinitionCreate,
    AgentDefinitionListResponse,
    AgentDefinitionResponse,
    AgentDefinitionUpdate,
    AgentMessageRequest,
    AgentRunListResponse,
    AgentRunResponse,
    AgentVersionListResponse,
    AgentVersionResponse,
)
from app.plugins.builtins.automation.services import (
    AgentService,
    RunLogService,
)

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v1/agents", tags=["agents"])


# ─── Helper Functions ───


def _agent_to_response(a: AgentDefinition) -> AgentDefinitionResponse:
    """Convert AgentDefinition model to response schema."""
    return AgentDefinitionResponse(
        id=str(a.id),
        name=a.name,
        description=a.description or "",
        llm_model=a.llm_model,
        system_prompt=a.system_prompt or "",
        tool_ids=[str(t) for t in (a.tool_ids or [])],
        heartbeat_interval_seconds=a.heartbeat_interval_seconds,
        mode=a.mode,
        is_active=a.is_active,
        max_executions_per_hour=a.max_executions_per_hour,
        max_duration_seconds=a.max_duration_seconds,
        budget_limit_usd=a.budget_limit_usd,
        created_by=str(a.created_by) if a.created_by else None,
        created_at=a.created_at.isoformat() if a.created_at else None,
        updated_at=a.updated_at.isoformat() if a.updated_at else None,
    )


def _agent_run_to_response(r: AgentRun) -> AgentRunResponse:
    """Convert AgentRun model to response schema."""
    return AgentRunResponse(
        id=str(r.id),
        agent_id=str(r.agent_id),
        status=r.status,
        started_at=r.started_at.isoformat() if r.started_at else "",
        completed_at=r.completed_at.isoformat() if r.completed_at else None,
        duration_seconds=r.duration_seconds,
        result=r.result,
        error=r.error,
        cost_usd=r.cost_usd,
        trigger_type=r.trigger_type,
        trigger_data=r.trigger_data or {},
        created_at=r.created_at.isoformat() if r.created_at else None,
    )


def _agent_version_to_response(v: AgentVersion) -> AgentVersionResponse:
    """Convert AgentVersion model to response schema."""
    return AgentVersionResponse(
        id=str(v.id),
        agent_id=str(v.agent_id),
        version_number=v.version_number,
        snapshot=v.snapshot or {},
        changed_by=str(v.changed_by) if v.changed_by else None,
        created_at=v.created_at.isoformat() if v.created_at else None,
    )


# ─── CRUD Endpoints ───


@router.get(
    "",
    dependencies=[Depends(require_permission("agents:read"))],
    response_model=AgentDefinitionListResponse,
)
async def list_agents(
    is_active: bool | None = Query(None),
    mode: str | None = Query(None),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """List agent definitions with optional filters."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    items, total = await AgentService.list(
        db, tenant_id, is_active=is_active, mode=mode, limit=limit, offset=offset
    )
    return AgentDefinitionListResponse(
        items=[_agent_to_response(a) for a in items],
        total=total,
    )


@router.post(
    "/",
    dependencies=[Depends(require_permission("agents:write"))],
    response_model=AgentDefinitionResponse,
    status_code=201,
)
async def create_agent(
    data: AgentDefinitionCreate,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Create a new agent definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    user_id = uuid.UUID(current_user["user_id"])
    agent = await AgentService.create(db, tenant_id, data.model_dump(), user_id=user_id)
    return _agent_to_response(agent)



@router.get(
    "/tools",
    dependencies=[Depends(require_permission("agents:read"))],
)
async def list_tools(
    current_user: dict[str, Any] = Depends(get_current_user),
):
    """List available tools from the tool registry."""
    try:
        from app.plugins.builtins.ai_assistant.tool_registry import (
            get_tool_registry,
        )

        registry = get_tool_registry()
        tools = registry.list_tools()
        return {
            "items": [
                {
                    "id": t.get("id", t.get("name", "")),
                    "name": t.get("name", ""),
                    "description": t.get("description", ""),
                    "plugin": t.get("plugin", ""),
                }
                for t in tools
            ],
            "total": len(tools),
        }
    except ImportError:
        return {"items": [], "total": 0, "note": "Tool registry not available"}
    except Exception as e:
        logger.exception("Failed to list tools")
        return {"items": [], "total": 0, "error": str(e)}


# ─── Recent Runs ───


@router.get(
    "/runs/recent",
    dependencies=[Depends(require_permission("agents:read"))],
    response_model=AgentRunListResponse,
)
async def list_recent_agent_runs(
    limit: int = Query(20, ge=1, le=100),
    status: str | None = Query(None),
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """List recent agent runs across all agents (for dashboard)."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    items, total = await RunLogService.list_agent_runs(
        db, tenant_id, agent_id=None, status=status, limit=limit, offset=0
    )
    return AgentRunListResponse(
        items=[_agent_run_to_response(r) for r in items],
        total=total,
    )
@router.get(
    "/{agent_id}",
    dependencies=[Depends(require_permission("agents:read"))],
    response_model=AgentDefinitionResponse,
)
async def get_agent(
    agent_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Get a single agent definition by ID."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(agent_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid agent ID")

    agent = await AgentService.get_by_id(db, tenant_id, aid)
    if agent is None:
        raise HTTPException(status_code=404, detail="Agent not found")
    return _agent_to_response(agent)


@router.patch(
    "/{agent_id}",
    dependencies=[Depends(require_permission("agents:write"))],
    response_model=AgentDefinitionResponse,
)
async def update_agent(
    agent_id: str,
    data: AgentDefinitionUpdate,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Update an existing agent definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    user_id = uuid.UUID(current_user["user_id"])
    try:
        aid = uuid.UUID(agent_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid agent ID")

    agent = await AgentService.update(
        db, tenant_id, aid, data.model_dump(exclude_none=True), user_id=user_id
    )
    if agent is None:
        raise HTTPException(status_code=404, detail="Agent not found")
    return _agent_to_response(agent)


@router.delete(
    "/{agent_id}",
    dependencies=[Depends(require_permission("agents:delete"))],
)
async def delete_agent(
    agent_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Delete an agent definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(agent_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid agent ID")

    success = await AgentService.delete(db, tenant_id, aid)
    if not success:
        raise HTTPException(status_code=404, detail="Agent not found")
    return {"status": "ok"}


# ─── Execute / Test-Run ───


@router.post(
    "/{agent_id}/execute",
    dependencies=[Depends(require_permission("agents:execute"))],
)
async def execute_agent(
    agent_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Execute an agent definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(agent_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid agent ID")

    agent = await AgentService.get_by_id(db, tenant_id, aid)
    if agent is None:
        raise HTTPException(status_code=404, detail="Agent not found")
    if not agent.is_active:
        raise HTTPException(status_code=400, detail="Agent is not active")

    # Create run log entry
    run = AgentRun(
        tenant_id=tenant_id,
        agent_id=aid,
        status="running",
        started_at=__import__("datetime").datetime.now(__import__("datetime").timezone.utc),
        trigger_type="manual",
        trigger_data={"triggered_by": str(uuid.UUID(current_user["user_id"]))},
    )
    db.add(run)
    await db.flush()

    # TODO: Execute agent asynchronously via Agent Runner
    run.status = "completed"
    run.completed_at = __import__("datetime").datetime.now(__import__("datetime").timezone.utc)
    run.duration_seconds = 0.0
    run.result = "Executed successfully (stub)"
    await db.flush()

    return {"status": "ok", "run_id": str(run.id)}


@router.post(
    "/{agent_id}/test-run",
    dependencies=[Depends(require_permission("agents:execute"))],
)
async def test_run_agent(
    agent_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Test-run an agent definition (validates config without full execution)."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(agent_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid agent ID")

    agent = await AgentService.get_by_id(db, tenant_id, aid)
    if agent is None:
        raise HTTPException(status_code=404, detail="Agent not found")

    return {
        "status": "ok",
        "agent": _agent_to_response(agent),
        "validation": {
            "valid": True,
            "llm_model": agent.llm_model,
            "tool_count": len(agent.tool_ids or []),
            "mode": agent.mode,
        },
    }


# ─── Runs ───


@router.get(
    "/{agent_id}/runs",
    dependencies=[Depends(require_permission("agents:read"))],
    response_model=AgentRunListResponse,
)
async def list_agent_runs(
    agent_id: str,
    status: str | None = Query(None),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """List run logs for an agent definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(agent_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid agent ID")

    items, total = await RunLogService.list_agent_runs(
        db, tenant_id, agent_id=aid, status=status, limit=limit, offset=offset
    )
    return AgentRunListResponse(
        items=[_agent_run_to_response(r) for r in items],
        total=total,
    )


# ─── Versions ───


@router.get(
    "/{agent_id}/versions",
    dependencies=[Depends(require_permission("agents:read"))],
    response_model=AgentVersionListResponse,
)
async def list_agent_versions(
    agent_id: str,
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """List version history for an agent definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(agent_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid agent ID")

    items, total = await AgentService.get_versions(
        db, tenant_id, aid, limit=limit, offset=offset
    )
    return AgentVersionListResponse(
        items=[_agent_version_to_response(v) for v in items],
        total=total,
    )


@router.post(
    "/{agent_id}/versions/{version_id}/restore",
    dependencies=[Depends(require_permission("agents:write"))],
    response_model=AgentDefinitionResponse,
)
async def restore_agent_version(
    agent_id: str,
    version_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Restore an agent definition from a specific version."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    user_id = uuid.UUID(current_user["user_id"])
    try:
        aid = uuid.UUID(agent_id)
        vid = uuid.UUID(version_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid ID")

    agent = await AgentService.restore_version(
        db, tenant_id, aid, vid, user_id=user_id
    )
    if agent is None:
        raise HTTPException(status_code=404, detail="Agent or version not found")
    return _agent_to_response(agent)


# ─── Tools ───


@router.post(
    "/{id}/send-message",
    dependencies=[Depends(require_permission("agents:execute"))],
)
async def send_agent_message_endpoint(
    id: str,
    body: AgentMessageRequest,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Send a message from one agent to another."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid agent ID")

    # Verify the source agent exists
    agent = await AgentService.get_by_id(db, tenant_id, aid)
    if agent is None:
        raise HTTPException(status_code=404, detail="Agent not found")
    if not agent.is_active:
        raise HTTPException(status_code=400, detail="Agent is not active")

    from app.plugins.builtins.automation.agent_comm import send_agent_message

    result = await send_agent_message(
        from_agent_id=str(aid),
        to_agent_name=body.to_agent_name,
        message=body.message,
        db=db,
        tenant_id=tenant_id,
    )
    return result

