# app/api/routes/agent_types.py
"""
AgentType configuration API endpoints.

Provides CRUD operations for managing AI agent type templates.
Agent types define the base configuration (model, personality, expertise)
from which agent instances are spawned for projects.

Authorization:
- Read endpoints: Any authenticated user
- Write endpoints (create, update, delete): Superusers only
"""

import logging
import os
from typing import Any
from uuid import UUID

from fastapi import APIRouter, Depends, Query, Request, status
from slowapi import Limiter
from slowapi.util import get_remote_address
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.dependencies.auth import get_current_user
from app.api.dependencies.permissions import require_superuser
from app.core.database import get_db
from app.core.exceptions import (
    DuplicateError,
    ErrorCode,
    NotFoundError,
)
from app.crud.syndarix.agent_type import agent_type as agent_type_crud
from app.models.user import User
from app.schemas.common import (
    MessageResponse,
    PaginatedResponse,
    PaginationParams,
    create_pagination_meta,
)
from app.schemas.syndarix import (
    AgentTypeCreate,
    AgentTypeResponse,
    AgentTypeUpdate,
)

router = APIRouter()
logger = logging.getLogger(__name__)

# Initialize limiter for this router
limiter = Limiter(key_func=get_remote_address)

# Use higher rate limits in test environment
IS_TEST = os.getenv("IS_TEST", "False") == "True"
RATE_MULTIPLIER = 100 if IS_TEST else 1


def _build_agent_type_response(
    agent_type: Any,
    instance_count: int = 0,
) -> AgentTypeResponse:
    """
    Build an AgentTypeResponse from a database model.

    Args:
        agent_type: AgentType model instance
        instance_count: Number of agent instances for this type

    Returns:
        AgentTypeResponse schema
    """
    return AgentTypeResponse(
        id=agent_type.id,
        name=agent_type.name,
        slug=agent_type.slug,
        description=agent_type.description,
        expertise=agent_type.expertise,
        personality_prompt=agent_type.personality_prompt,
        primary_model=agent_type.primary_model,
        fallback_models=agent_type.fallback_models,
        model_params=agent_type.model_params,
        mcp_servers=agent_type.mcp_servers,
        tool_permissions=agent_type.tool_permissions,
        is_active=agent_type.is_active,
        created_at=agent_type.created_at,
        updated_at=agent_type.updated_at,
        instance_count=instance_count,
    )


# ===== Write Endpoints (Admin Only) =====


@router.post(
    "",
    response_model=AgentTypeResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Create Agent Type",
    description="Create a new agent type configuration (admin only)",
    operation_id="create_agent_type",
)
@limiter.limit(f"{20 * RATE_MULTIPLIER}/minute")
async def create_agent_type(
    request: Request,
    agent_type_in: AgentTypeCreate,
    admin: User = Depends(require_superuser),
    db: AsyncSession = Depends(get_db),
) -> Any:
    """
    Create a new agent type configuration.

    Agent types define templates for AI agents including:
    - Model configuration (primary model, fallback models, parameters)
    - Personality and expertise areas
    - MCP server integrations and tool permissions

    Requires superuser privileges.

    Args:
        request: FastAPI request object
        agent_type_in: Agent type creation data
        admin: Authenticated superuser
        db: Database session

    Returns:
        The created agent type configuration

    Raises:
        DuplicateError: If slug already exists
    """
    try:
        agent_type = await agent_type_crud.create(db, obj_in=agent_type_in)
        logger.info(
            f"Admin {admin.email} created agent type: {agent_type.name} "
            f"(slug: {agent_type.slug})"
        )
        return _build_agent_type_response(agent_type, instance_count=0)

    except ValueError as e:
        logger.warning(f"Failed to create agent type: {e!s}")
        raise DuplicateError(
            message=str(e),
            error_code=ErrorCode.ALREADY_EXISTS,
            field="slug",
        )
    except Exception as e:
        logger.error(f"Error creating agent type: {e!s}", exc_info=True)
        raise


@router.patch(
    "/{agent_type_id}",
    response_model=AgentTypeResponse,
    summary="Update Agent Type",
    description="Update an existing agent type configuration (admin only)",
    operation_id="update_agent_type",
)
@limiter.limit(f"{30 * RATE_MULTIPLIER}/minute")
async def update_agent_type(
    request: Request,
    agent_type_id: UUID,
    agent_type_in: AgentTypeUpdate,
    admin: User = Depends(require_superuser),
    db: AsyncSession = Depends(get_db),
) -> Any:
    """
    Update an existing agent type configuration.

    Partial updates are supported - only provided fields will be updated.

    Requires superuser privileges.

    Args:
        request: FastAPI request object
        agent_type_id: UUID of the agent type to update
        agent_type_in: Agent type update data
        admin: Authenticated superuser
        db: Database session

    Returns:
        The updated agent type configuration

    Raises:
        NotFoundError: If agent type not found
        DuplicateError: If new slug already exists
    """
    try:
        # Verify agent type exists
        result = await agent_type_crud.get_with_instance_count(
            db, agent_type_id=agent_type_id
        )
        if not result:
            raise NotFoundError(
                message=f"Agent type {agent_type_id} not found",
                error_code=ErrorCode.NOT_FOUND,
            )

        existing_type = result["agent_type"]
        instance_count = result["instance_count"]

        # Perform update
        updated_type = await agent_type_crud.update(
            db, db_obj=existing_type, obj_in=agent_type_in
        )

        logger.info(
            f"Admin {admin.email} updated agent type: {updated_type.name} "
            f"(id: {agent_type_id})"
        )

        return _build_agent_type_response(updated_type, instance_count=instance_count)

    except NotFoundError:
        raise
    except ValueError as e:
        logger.warning(f"Failed to update agent type {agent_type_id}: {e!s}")
        raise DuplicateError(
            message=str(e),
            error_code=ErrorCode.ALREADY_EXISTS,
            field="slug",
        )
    except Exception as e:
        logger.error(f"Error updating agent type {agent_type_id}: {e!s}", exc_info=True)
        raise


@router.delete(
    "/{agent_type_id}",
    response_model=MessageResponse,
    summary="Deactivate Agent Type",
    description="Deactivate an agent type (soft delete, admin only)",
    operation_id="deactivate_agent_type",
)
@limiter.limit(f"{10 * RATE_MULTIPLIER}/minute")
async def deactivate_agent_type(
    request: Request,
    agent_type_id: UUID,
    admin: User = Depends(require_superuser),
    db: AsyncSession = Depends(get_db),
) -> Any:
    """
    Deactivate an agent type (soft delete).

    This sets is_active=False rather than deleting the record,
    preserving referential integrity with existing agent instances.

    Requires superuser privileges.

    Args:
        request: FastAPI request object
        agent_type_id: UUID of the agent type to deactivate
        admin: Authenticated superuser
        db: Database session

    Returns:
        Success message

    Raises:
        NotFoundError: If agent type not found
    """
    try:
        deactivated = await agent_type_crud.deactivate(db, agent_type_id=agent_type_id)

        if not deactivated:
            raise NotFoundError(
                message=f"Agent type {agent_type_id} not found",
                error_code=ErrorCode.NOT_FOUND,
            )

        logger.info(
            f"Admin {admin.email} deactivated agent type: {deactivated.name} "
            f"(id: {agent_type_id})"
        )

        return MessageResponse(
            success=True,
            message=f"Agent type '{deactivated.name}' has been deactivated",
        )

    except NotFoundError:
        raise
    except Exception as e:
        logger.error(
            f"Error deactivating agent type {agent_type_id}: {e!s}", exc_info=True
        )
        raise


# ===== Read Endpoints (Authenticated Users) =====


@router.get(
    "",
    response_model=PaginatedResponse[AgentTypeResponse],
    summary="List Agent Types",
    description="Get paginated list of active agent types",
    operation_id="list_agent_types",
)
@limiter.limit(f"{60 * RATE_MULTIPLIER}/minute")
async def list_agent_types(
    request: Request,
    pagination: PaginationParams = Depends(),
    is_active: bool = Query(True, description="Filter by active status"),
    search: str | None = Query(None, description="Search by name, slug, description"),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> Any:
    """
    List all agent types with pagination and filtering.

    By default, returns only active agent types. Set is_active=false
    to include deactivated types (useful for admin views).

    Args:
        request: FastAPI request object
        pagination: Pagination parameters (page, limit)
        is_active: Filter by active status (default: True)
        search: Optional search term for name, slug, description
        current_user: Authenticated user
        db: Database session

    Returns:
        Paginated list of agent types with instance counts
    """
    try:
        # Get agent types with instance counts
        results, total = await agent_type_crud.get_multi_with_instance_counts(
            db,
            skip=pagination.offset,
            limit=pagination.limit,
            is_active=is_active,
            search=search,
        )

        # Build response objects
        agent_types_response = [
            _build_agent_type_response(
                item["agent_type"],
                instance_count=item["instance_count"],
            )
            for item in results
        ]

        pagination_meta = create_pagination_meta(
            total=total,
            page=pagination.page,
            limit=pagination.limit,
            items_count=len(agent_types_response),
        )

        return PaginatedResponse(data=agent_types_response, pagination=pagination_meta)

    except Exception as e:
        logger.error(f"Error listing agent types: {e!s}", exc_info=True)
        raise


@router.get(
    "/{agent_type_id}",
    response_model=AgentTypeResponse,
    summary="Get Agent Type",
    description="Get agent type details by ID",
    operation_id="get_agent_type",
)
@limiter.limit(f"{100 * RATE_MULTIPLIER}/minute")
async def get_agent_type(
    request: Request,
    agent_type_id: UUID,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> Any:
    """
    Get detailed information about a specific agent type.

    Args:
        request: FastAPI request object
        agent_type_id: UUID of the agent type
        current_user: Authenticated user
        db: Database session

    Returns:
        Agent type details with instance count

    Raises:
        NotFoundError: If agent type not found
    """
    try:
        result = await agent_type_crud.get_with_instance_count(
            db, agent_type_id=agent_type_id
        )

        if not result:
            raise NotFoundError(
                message=f"Agent type {agent_type_id} not found",
                error_code=ErrorCode.NOT_FOUND,
            )

        return _build_agent_type_response(
            result["agent_type"],
            instance_count=result["instance_count"],
        )

    except NotFoundError:
        raise
    except Exception as e:
        logger.error(f"Error getting agent type {agent_type_id}: {e!s}", exc_info=True)
        raise


@router.get(
    "/slug/{slug}",
    response_model=AgentTypeResponse,
    summary="Get Agent Type by Slug",
    description="Get agent type details by slug",
    operation_id="get_agent_type_by_slug",
)
@limiter.limit(f"{100 * RATE_MULTIPLIER}/minute")
async def get_agent_type_by_slug(
    request: Request,
    slug: str,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> Any:
    """
    Get detailed information about an agent type by its slug.

    Slugs are human-readable identifiers like "product-owner" or "backend-engineer".
    Useful for referencing agent types in configuration files or APIs.

    Args:
        request: FastAPI request object
        slug: Slug identifier of the agent type
        current_user: Authenticated user
        db: Database session

    Returns:
        Agent type details with instance count

    Raises:
        NotFoundError: If agent type not found
    """
    try:
        agent_type = await agent_type_crud.get_by_slug(db, slug=slug)

        if not agent_type:
            raise NotFoundError(
                message=f"Agent type with slug '{slug}' not found",
                error_code=ErrorCode.NOT_FOUND,
            )

        # Get instance count separately
        result = await agent_type_crud.get_with_instance_count(
            db, agent_type_id=agent_type.id
        )
        instance_count = result["instance_count"] if result else 0

        return _build_agent_type_response(agent_type, instance_count=instance_count)

    except NotFoundError:
        raise
    except Exception as e:
        logger.error(f"Error getting agent type by slug '{slug}': {e!s}", exc_info=True)
        raise
