from fastapi import Path from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel, Field, field_validator from typing import List, Optional from uuid import UUID from src.config import logger from src.services import RFPService from datetime import datetime from src.models import RFPStatus class RFP(BaseModel): id: UUID solicitation_no: str solicitation_title: str google_drive_id: Optional[str] = None status: RFPStatus created_at: datetime updated_at: datetime class ResponseRFP(BaseModel): status: str data: Optional[List[RFP]] class REFPSingleResponse(BaseModel): status: str data: RFP class RFPRequest(BaseModel): solicitation_no: str solicitation_title: str google_drive_id: Optional[str] = None status: Optional[RFPStatus] = None class RFPUpdateRequest(BaseModel): solicitation_no: Optional[str] = None solicitation_title: Optional[str] = None google_drive_id: Optional[str] = None status: Optional[RFPStatus] = None class RFPDeleteResponse(BaseModel): status: str class RFPController: def __init__(self): self.__rfp_service = RFPService self.router = APIRouter() self.router.add_api_route( "/rfps", self.get_rfps, methods=["GET"], response_model=ResponseRFP, tags=["RFPs"], ) self.router.add_api_route( "/rfps/{rfp_id}", self.get_rfps_by_id, methods=["GET"], response_model=REFPSingleResponse, tags=["RFPs by ID"], ) self.router.add_api_route( "/rfps", self.create_rfp, methods=["POST"], response_model=REFPSingleResponse, tags=["RFPs"], ) self.router.add_api_route( "/rfps/{rfp_id}", self.update_rfp, methods=["PATCH"], response_model=REFPSingleResponse, tags=["RFPs by ID"], ) self.router.add_api_route( "/rfps/{rfp_id}", self.delete_rfp, methods=["DELETE"], response_model=RFPDeleteResponse, tags=["RFPs by ID"], ) async def get_rfps(self, status: Optional[RFPStatus] = Query(None)): async with self.__rfp_service() as service: try: rfps = await service.get_rfps(status=status) return ResponseRFP(status="success", data=[RFP(**rfp) for rfp in rfps]) except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve RFPs.") async def get_rfps_by_id(self, rfp_id: UUID = Path(...)): async with self.__rfp_service() as service: try: rfps = await service.get_rfps(rfp_id=rfp_id) rfp = rfps[0] return REFPSingleResponse(status="success", data=RFP(**rfp)) except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve RFPs.") async def create_rfp(self, rfp: RFPRequest): async with self.__rfp_service() as service: try: rfp = await service.create_rfp(rfp.model_dump(exclude_unset=True)) return REFPSingleResponse(status="success", data=RFP(**rfp)) except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to create RFP.") async def update_rfp(self, rfp: RFPUpdateRequest, rfp_id: UUID = Path(...)): async with self.__rfp_service() as service: try: rfp = await service.update_rfp( rfp_id=rfp_id, rfp=rfp.model_dump(exclude_unset=True) ) return REFPSingleResponse(status="success", data=RFP(**rfp)) except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to update RFP.") async def delete_rfp(self, rfp_id: UUID = Path(...)): async with self.__rfp_service() as service: try: status = await service.delete_rfp(rfp_id) if not status: raise HTTPException(status_code=404, detail="RFP not found.") return RFPDeleteResponse(status="success") except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to delete RFP.")