"""EDA Agent API routes."""
from __future__ import annotations

from typing import List
import uuid

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

from eda_agent.engine import EDAEngine
from eda_agent.api.deps_auth import ensure_agent_header_match
from eda_agent.db.connection import get_db
from eda_agent.models.persistence import AgentProfile
from eda_agent.schemas import (
    AgentCreateRequest,
    AgentListItemResponse,
    AgentUpdateRequest,
    AgentStateResponse,
    AgentSummaryResponse,
    BindNormsRequest,
    DecideRequest,
    DecisionResponse,
    ExecuteRequest,
    ExecutionResponse,
    PerceiveRequest,
    TraceResponse,
)
from eda_agent.services.agent_service import AgentService
from eda_agent.services.field_service import FieldService
from eda_agent.infra.normbase_client import HttpNormbaseClient
from eda_agent.infra.llm import get_llm_provider
from eda_agent.config import settings
from eda_agent.services.runtime_service import RuntimeService
from eda_agent.services.trace_service import TraceService

router = APIRouter(prefix="/agents", tags=["eda-agent"])

_field_service = FieldService()
_trace_service = TraceService()
_agent_service = AgentService(field_service=_field_service)


def _build_engine(_profile: AgentProfile) -> EDAEngine:
    provider = get_llm_provider()
    normbase = HttpNormbaseClient(
        base_url=settings.eda_normbase_base_url,
        retrieve_timeout_seconds=settings.eda_normbase_retrieve_timeout_seconds,
    )
    return EDAEngine(normbase_client=normbase, llm_provider=provider)


def _get_runtime_service() -> RuntimeService:
    # Keep runtime dependency overridable for tests via monkeypatch(_build_engine).
    return RuntimeService(
        agent_service=_agent_service,
        field_service=_field_service,
        trace_service=_trace_service,
        engine_builder=_build_engine,
    )


@router.post("", response_model=AgentSummaryResponse, status_code=status.HTTP_201_CREATED)
async def create_agent(
    request: AgentCreateRequest,
    db: AsyncSession = Depends(get_db),
):
    return await _agent_service.create_agent(db, request)


@router.get("", response_model=List[AgentListItemResponse])
async def list_agents(
    limit: int = Query(100, ge=1, le=500),
    db: AsyncSession = Depends(get_db),
):
    return await _agent_service.list_agents(db, limit=limit)


@router.get("/{agent_id}", response_model=AgentStateResponse)
async def get_agent_state(
    agent_id: uuid.UUID,
    raw_request: Request,
    db: AsyncSession = Depends(get_db),
):
    ensure_agent_header_match(agent_id, raw_request.headers.get("x-agent-id"))
    return await _agent_service.get_agent_state(db, agent_id=agent_id)


@router.patch("/{agent_id}", response_model=AgentSummaryResponse)
async def update_agent(
    agent_id: uuid.UUID,
    request: AgentUpdateRequest,
    raw_request: Request,
    db: AsyncSession = Depends(get_db),
):
    ensure_agent_header_match(agent_id, raw_request.headers.get("x-agent-id"))
    return await _agent_service.update_agent(db, agent_id=agent_id, request=request)


@router.delete("/{agent_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_agent(
    agent_id: uuid.UUID,
    raw_request: Request,
    db: AsyncSession = Depends(get_db),
):
    ensure_agent_header_match(agent_id, raw_request.headers.get("x-agent-id"))
    await _agent_service.delete_agent(db, agent_id=agent_id)


@router.post("/{agent_id}/perceive", response_model=AgentStateResponse)
async def perceive(
    agent_id: uuid.UUID,
    request: PerceiveRequest,
    raw_request: Request,
    db: AsyncSession = Depends(get_db),
):
    ensure_agent_header_match(agent_id, raw_request.headers.get("x-agent-id"))
    return await _get_runtime_service().perceive(
        db,
        agent_id=agent_id,
        request=request,
        raw_request=raw_request,
    )


@router.post("/{agent_id}/bind_norms", response_model=AgentStateResponse)
async def bind_norms(
    agent_id: uuid.UUID,
    request: BindNormsRequest,
    raw_request: Request,
    db: AsyncSession = Depends(get_db),
):
    ensure_agent_header_match(agent_id, raw_request.headers.get("x-agent-id"))
    return await _get_runtime_service().bind_norms(
        db,
        agent_id=agent_id,
        request=request,
        raw_request=raw_request,
    )


@router.post("/{agent_id}/decide", response_model=DecisionResponse)
async def decide(
    agent_id: uuid.UUID,
    request: DecideRequest,
    raw_request: Request,
    db: AsyncSession = Depends(get_db),
):
    ensure_agent_header_match(agent_id, raw_request.headers.get("x-agent-id"))
    return await _get_runtime_service().decide(
        db,
        agent_id=agent_id,
        request=request,
        raw_request=raw_request,
    )


@router.post("/{agent_id}/execute", response_model=ExecutionResponse)
async def execute(
    agent_id: uuid.UUID,
    request: ExecuteRequest,
    raw_request: Request,
    db: AsyncSession = Depends(get_db),
):
    ensure_agent_header_match(agent_id, raw_request.headers.get("x-agent-id"))
    return await _get_runtime_service().execute(
        db,
        agent_id=agent_id,
        request=request,
        raw_request=raw_request,
    )


@router.get("/{agent_id}/traces", response_model=List[TraceResponse])
async def get_traces(
    agent_id: uuid.UUID,
    raw_request: Request,
    limit: int = Query(20, ge=1, le=200),
    db: AsyncSession = Depends(get_db),
):
    ensure_agent_header_match(agent_id, raw_request.headers.get("x-agent-id"))
    return await _get_runtime_service().get_traces(db, agent_id=agent_id, limit=limit)

