from fastapi import APIRouter, Depends, HTTPException, Query, BackgroundTasks from typing import Optional, List from ...application.use_cases.obter_ranking import ObterRankingUseCase from ...application.use_cases.obter_consultor import ObterConsultorUseCase from ...application.mappers import RankingMapper from ...infrastructure.repositories.consultor_repository_impl import ConsultorRepositoryImpl from ..schemas.consultor_schema import ( RankingResponseSchema, RankingDetalhadoResponseSchema, ConsultorDetalhadoSchema, ConsultorResumoSchema, ) from ..schemas.ranking_schema import ( RankingPaginadoResponseSchema, ConsultorRankingResumoSchema, EstatisticasRankingSchema, JobStatusSchema, ProcessarRankingRequestSchema, ProcessarRankingResponseSchema, ConsultaNomeSchema, ) from .dependencies import get_repository, get_ranking_repository, get_processar_job from ...application.jobs.job_status import job_status router = APIRouter(prefix="/api/v1", tags=["ranking"]) @router.get("/ranking", response_model=RankingResponseSchema) async def obter_ranking( limite: int = Query(default=100, ge=1, le=1000, description="Limite de consultores"), offset: int = Query(default=0, ge=0, description="Offset para paginação"), componente: Optional[str] = Query( default=None, description="Filtrar por bloco (a, c, d)" ), repository: ConsultorRepositoryImpl = Depends(get_repository), ): use_case = ObterRankingUseCase(repository=repository) consultores_dto = await use_case.executar(limite=limite, componente=componente) total = await repository.contar_total() consultores_schema = [ ConsultorResumoSchema(**vars(dto)) for dto in consultores_dto ] return RankingResponseSchema( total=total, limite=limite, offset=offset, consultores=consultores_schema ) @router.get("/ranking/detalhado", response_model=RankingDetalhadoResponseSchema) async def obter_ranking_detalhado( limite: int = Query(default=100, ge=1, le=1000, description="Limite de consultores"), componente: Optional[str] = Query( default=None, description="Filtrar por bloco (a, c, d)" ), repository: ConsultorRepositoryImpl = Depends(get_repository), ): use_case = ObterRankingUseCase(repository=repository) consultores_dto = await use_case.executar_detalhado(limite=limite, componente=componente) total = await repository.contar_total() consultores_schema = [ ConsultorDetalhadoSchema(**dto.to_dict()) for dto in consultores_dto ] return RankingDetalhadoResponseSchema(total=total, limite=limite, consultores=consultores_schema) @router.get("/consultor/{id_pessoa}", response_model=ConsultorDetalhadoSchema) async def obter_consultor( id_pessoa: int, repository: ConsultorRepositoryImpl = Depends(get_repository), ): use_case = ObterConsultorUseCase(repository=repository) consultor = await use_case.executar(id_pessoa=id_pessoa) if not consultor: raise HTTPException(status_code=404, detail=f"Consultor {id_pessoa} não encontrado") return consultor @router.get("/health") async def health_check(): return {"status": "ok", "message": "API Ranking CAPES funcionando"} @router.get("/ranking/paginado", response_model=RankingPaginadoResponseSchema) async def ranking_paginado( page: int = Query(default=1, ge=1, description="Número da página"), size: int = Query(default=50, ge=1, le=1000, description="Tamanho da página (máx 1000)"), ativo: Optional[bool] = Query(default=None, description="Filtrar por status ativo"), ranking_repo = Depends(get_ranking_repository), ): total = ranking_repo.contar_total(filtro_ativo=ativo) consultores = ranking_repo.buscar_paginado(page=page, size=size, filtro_ativo=ativo) total_pages = (total + size - 1) // size consultores_schema = [RankingMapper.consultor_ranking_to_schema(c) for c in consultores] return RankingPaginadoResponseSchema( total=total, page=page, size=size, total_pages=total_pages, consultores=consultores_schema ) @router.get("/ranking/busca", response_model=List[ConsultaNomeSchema]) async def buscar_por_nome( nome: str = Query(..., min_length=3, description="Nome (ou parte) para buscar"), limit: int = Query(default=5, ge=1, le=20, description="Limite de resultados"), ranking_repo = Depends(get_ranking_repository), ): resultados = ranking_repo.buscar_por_nome(nome=nome, limit=limit) return [ ConsultaNomeSchema( id_pessoa=r["ID_PESSOA"], nome=r["NOME"], posicao=r["POSICAO"], pontuacao_total=float(r["PONTUACAO_TOTAL"]), ) for r in resultados ] @router.get("/ranking/estatisticas", response_model=EstatisticasRankingSchema) async def ranking_estatisticas( ranking_repo = Depends(get_ranking_repository), ): estatisticas = ranking_repo.obter_estatisticas() distribuicao = ranking_repo.obter_distribuicao() return EstatisticasRankingSchema( total_consultores=estatisticas.get("total_consultores", 0), total_ativos=estatisticas.get("total_ativos", 0), total_inativos=estatisticas.get("total_inativos", 0), ultima_atualizacao=estatisticas.get("ultima_atualizacao"), pontuacao_media=estatisticas.get("pontuacao_media", 0), pontuacao_maxima=estatisticas.get("pontuacao_maxima", 0), pontuacao_minima=estatisticas.get("pontuacao_minima", 0), media_blocos=estatisticas.get("media_componentes", {}), distribuicao=distribuicao ) @router.get("/ranking/status", response_model=JobStatusSchema) async def status_processamento(): return JobStatusSchema(**job_status.to_dict()) @router.post("/ranking/processar", response_model=ProcessarRankingResponseSchema) async def processar_ranking( background_tasks: BackgroundTasks, request: ProcessarRankingRequestSchema = ProcessarRankingRequestSchema(), job = Depends(get_processar_job), ): if job_status.is_running: raise HTTPException(status_code=409, detail="Job já está em execução") background_tasks.add_task(job.executar, limpar_antes=request.limpar_antes) return ProcessarRankingResponseSchema( sucesso=True, mensagem="Processamento do ranking iniciado em background", job_id="ranking_job" )