"""Platform API endpoints for multi-tenant agent access."""

import json
import uuid
from typing import Annotated, Any

from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field

from pydanticai_multiagent.config import settings
from pydanticai_multiagent.dependencies import SearchDeps
from pydanticai_multiagent.dependencies.mocks import MockCache, MockHttpClient, MockVectorStore
from pydanticai_multiagent.services import AgentRegistry, ConversationService, Tenant

router = APIRouter(prefix="/platform")


# Request/Response models
class ChatRequest(BaseModel):
    """Request to chat with an agent."""

    prompt: str = Field(..., description="The message to send")
    agent: str | None = Field(default=None, description="Agent name (uses default if omitted)")
    conversation_id: str | None = Field(default=None, description="Conversation ID for multi-turn")


class ChatResponse(BaseModel):
    """Response from an agent."""

    response: str
    conversation_id: str
    agent: str
    model: str


class AgentInfo(BaseModel):
    """Information about an available agent."""

    name: str
    description: str | None = None


# Dependencies
def get_tenant(request: Request) -> Tenant:
    """Get the authenticated tenant from request state."""
    tenant = getattr(request.state, "tenant", None)
    if not tenant:
        raise HTTPException(
            status_code=401,
            detail="Valid tenant API key required",
        )
    return tenant


def get_agent_registry(request: Request) -> AgentRegistry:
    """Get the agent registry from app state."""
    return request.app.state.agent_registry


def get_conversation_service(request: Request) -> ConversationService:
    """Get conversation service from app state."""
    return request.app.state.conversation_service


def get_mcp_toolsets(request: Request) -> list[Any]:
    """Get platform-wide MCP toolsets from app state."""
    return getattr(request.app.state, "mcp_toolsets", []) or []


async def get_deps_for_tenant(request: Request, tenant: Tenant) -> SearchDeps:
    """Create dependencies for a tenant request.

    Uses real implementations from app.state when available,
    falls back to mocks for development/testing.
    """
    # Use real clients from app state, fall back to mocks
    http_client = getattr(request.app.state, "http_client", None) or MockHttpClient()
    cache = getattr(request.app.state, "cache", None) or MockCache()
    db = getattr(request.app.state, "db", None)

    return SearchDeps(
        http_client=http_client,
        db=db,
        cache=cache,
        user_id=f"tenant:{tenant.id}",
        vector_store=MockVectorStore(),  # Real impl in knowledge base feature
        search_api_key=settings.search_api_key,
    )


def generate_conversation_id() -> str:
    """Generate a unique conversation ID."""
    return f"conv_{uuid.uuid4().hex[:12]}"


# Endpoints
@router.get("/agents", response_model=list[AgentInfo])
async def list_agents(
    request: Request,
    tenant: Annotated[Tenant, Depends(get_tenant)],
    registry: Annotated[AgentRegistry, Depends(get_agent_registry)],
) -> list[AgentInfo]:
    """List agents available to this tenant."""
    db = getattr(request.app.state, "db", None)

    # Try to get tenant-specific agents from database
    if db:
        from pydanticai_multiagent.services import TenantService
        tenant_service = TenantService(db)
        agents = await tenant_service.get_tenant_agents(tenant.id)
        if agents:
            return [AgentInfo(name=a["name"], description=a["description"]) for a in agents]

    # Fall back to all registered agents (for tenants without specific grants)
    all_agents = await registry.list_agents()
    return [AgentInfo(name=a.name, description=a.description) for a in all_agents]


@router.post("/chat", response_model=ChatResponse)
async def chat(
    request: Request,
    body: ChatRequest,
    tenant: Annotated[Tenant, Depends(get_tenant)],
    registry: Annotated[AgentRegistry, Depends(get_agent_registry)],
    service: Annotated[ConversationService, Depends(get_conversation_service)],
) -> ChatResponse:
    """Send a message to an agent.

    If no agent is specified, uses the tenant's default agent.
    Returns the complete response (use /chat/stream for streaming).
    """
    # Get the agent
    agent_name = body.agent
    if not agent_name:
        # Get default agent for tenant
        result = await registry.get_tenant_default_agent(tenant.id)
        if not result:
            # Fall back to first available
            agents = await registry.list_agents()
            if not agents:
                raise HTTPException(status_code=404, detail="No agents available")
            agent_name = agents[0].name
            result = await registry.get_agent_for_tenant(agent_name, tenant.id)
    else:
        result = await registry.get_agent_for_tenant(agent_name, tenant.id)

    if not result:
        # Try to get agent directly (for development without tenant_agents table)
        agent = await registry.get_agent(agent_name or "router")
        if not agent:
            raise HTTPException(
                status_code=403,
                detail=f"Agent '{agent_name}' not available for this tenant",
            )
        model = tenant.default_model
    else:
        agent, model = result

    # Generate conversation ID
    conversation_id = body.conversation_id or generate_conversation_id()

    # Get deps for this tenant
    deps = await get_deps_for_tenant(request, tenant)

    # Check token limit before running agent
    if tenant.monthly_token_limit:
        tracker = getattr(request.app.state, "usage_tracker", None)
        if tracker:
            within_limit, tokens_used = await tracker.check_limit(
                user_id=f"tenant:{tenant.id}",
                token_limit=tenant.monthly_token_limit,
                period_days=30,
            )
            if not within_limit:
                raise HTTPException(
                    status_code=429,
                    detail={
                        "error": "Monthly token limit exceeded",
                        "tokens_used": tokens_used,
                        "limit": tenant.monthly_token_limit,
                    },
                )

    # Get platform-wide MCP toolsets
    mcp_toolsets = get_mcp_toolsets(request)

    # Run the agent with conversation history
    response = await service.chat_sync(
        conversation_id=conversation_id,
        user_id=f"tenant:{tenant.id}",
        prompt=body.prompt,
        agent=agent,
        deps=deps,
        agent_name=agent_name or "default",
        model=model,
        toolsets=mcp_toolsets,
    )

    return ChatResponse(
        response=response,
        conversation_id=conversation_id,
        agent=agent_name or "default",
        model=model,
    )


@router.post("/chat/stream")
async def chat_stream(
    request: Request,
    body: ChatRequest,
    tenant: Annotated[Tenant, Depends(get_tenant)],
    registry: Annotated[AgentRegistry, Depends(get_agent_registry)],
    service: Annotated[ConversationService, Depends(get_conversation_service)],
) -> StreamingResponse:
    """Stream a response from an agent.

    Returns Server-Sent Events (SSE) with response chunks.
    """
    # Get the agent (same logic as /chat)
    agent_name = body.agent
    result = None

    if agent_name:
        result = await registry.get_agent_for_tenant(agent_name, tenant.id)

    if not result:
        agent = await registry.get_agent(agent_name or "router")
        if not agent:
            raise HTTPException(
                status_code=403,
                detail=f"Agent '{agent_name}' not available",
            )
        model = tenant.default_model
    else:
        agent, model = result

    conversation_id = body.conversation_id or generate_conversation_id()
    deps = await get_deps_for_tenant(request, tenant)

    # Check token limit before running agent
    if tenant.monthly_token_limit:
        tracker = getattr(request.app.state, "usage_tracker", None)
        if tracker:
            within_limit, tokens_used = await tracker.check_limit(
                user_id=f"tenant:{tenant.id}",
                token_limit=tenant.monthly_token_limit,
                period_days=30,
            )
            if not within_limit:
                raise HTTPException(
                    status_code=429,
                    detail={
                        "error": "Monthly token limit exceeded",
                        "tokens_used": tokens_used,
                        "limit": tenant.monthly_token_limit,
                    },
                )

    # Get platform-wide MCP toolsets
    mcp_toolsets = get_mcp_toolsets(request)

    async def event_stream():
        """Generate SSE events."""
        async for chunk in service.chat(
            conversation_id=conversation_id,
            user_id=f"tenant:{tenant.id}",
            prompt=body.prompt,
            agent=agent,
            deps=deps,
            agent_name=agent_name or "default",
            model=model,
            toolsets=mcp_toolsets,
        ):
            data = json.dumps({
                "chunk": chunk,
                "conversation_id": conversation_id,
                "agent": agent_name or "default",
            })
            yield f"data: {data}\n\n"

        # Send final message
        final_data = json.dumps({
            "done": True,
            "conversation_id": conversation_id,
            "agent": agent_name or "default",
            "model": model,
        })
        yield f"data: {final_data}\n\n"

    return StreamingResponse(
        event_stream(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",
        },
    )


@router.delete("/conversations/{conversation_id}")
async def clear_conversation(
    conversation_id: str,
    tenant: Annotated[Tenant, Depends(get_tenant)],
    service: Annotated[ConversationService, Depends(get_conversation_service)],
) -> dict[str, str]:
    """Clear a conversation's history."""
    deleted = await service.clear_conversation(conversation_id)
    return {
        "status": "cleared" if deleted else "not_found",
        "conversation_id": conversation_id,
    }


@router.get("/usage")
async def get_usage(
    request: Request,
    tenant: Annotated[Tenant, Depends(get_tenant)],
    days: int = 30,
) -> dict[str, Any]:
    """Get usage statistics for this tenant."""
    from pydanticai_multiagent.services import UsageTracker

    db = getattr(request.app.state, "db", None)
    if not db:
        return {"error": "Usage tracking requires database"}

    tracker = UsageTracker(db)
    summary = await tracker.get_user_summary(f"tenant:{tenant.id}", days)

    return {
        "tenant_id": tenant.id,
        "period_days": days,
        "total_requests": summary.total_requests,
        "total_tokens": summary.total_tokens,
        "estimated_cost_usd": summary.estimated_cost,
        "by_model": summary.by_model,
        "by_agent": summary.by_agent,
    }


class ConversationSummary(BaseModel):
    """Summary of a conversation."""

    conversation_id: str
    created_at: str | None = None
    updated_at: str | None = None
    message_count: int = 0


class ConversationHistory(BaseModel):
    """Full conversation history."""

    conversation_id: str
    messages: list[dict[str, Any]]


@router.get("/conversations", response_model=list[ConversationSummary])
async def list_conversations(
    tenant: Annotated[Tenant, Depends(get_tenant)],
    service: Annotated[ConversationService, Depends(get_conversation_service)],
    limit: int = 20,
) -> list[ConversationSummary]:
    """List recent conversations for this tenant."""
    conversations = await service.list_conversations(
        user_id=f"tenant:{tenant.id}",
        limit=limit,
    )

    return [
        ConversationSummary(
            conversation_id=c.get("conversation_id", ""),
            created_at=str(c.get("created_at")) if c.get("created_at") else None,
            updated_at=str(c.get("updated_at")) if c.get("updated_at") else None,
            message_count=c.get("message_count", 0),
        )
        for c in conversations
    ]


@router.get("/conversations/{conversation_id}", response_model=ConversationHistory)
async def get_conversation(
    conversation_id: str,
    tenant: Annotated[Tenant, Depends(get_tenant)],
    service: Annotated[ConversationService, Depends(get_conversation_service)],
) -> ConversationHistory:
    """Get the full history of a conversation."""
    context = await service.get_context(
        conversation_id=conversation_id,
        user_id=f"tenant:{tenant.id}",
    )

    # Convert ModelMessage objects to dicts for JSON serialization
    messages = []
    for msg in context.messages:
        if hasattr(msg, "model_dump"):
            messages.append(msg.model_dump())
        else:
            messages.append({"content": str(msg)})

    return ConversationHistory(
        conversation_id=conversation_id,
        messages=messages,
    )
