from datetime import datetime, date from dis import Positions from typing import Type from fastapi import APIRouter, Depends, HTTPException, Query from fastapi.responses import JSONResponse import uuid from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from typing import Optional from candidate_forms.plugins import definitions_payload from candidate_forms.views import CandidateForm from db_setup import get_session from users.permissions import PermissionTag, require_permission from candidate_forms.enums import EmploymentType, Position, ReplacementFor, InternalRecommendate from candidate_forms.views import RequisitionForm router = APIRouter() class RequisitionFormCreate(BaseModel): form_type: str = "requisition" position:Position replacement_for:Optional[ReplacementFor] refferal_by:Optional[InternalRecommendate] initiated_by:Optional[str] initiated_date:Optional[date] recommended_by:Optional[str] recommended_date:Optional[date] approved_by_hr:Optional[bool] approved_by_date_hr:Optional[date] approved_by_vp:Optional[bool] approved_by_date_vp:Optional[date] approved_by_svp:Optional[bool] approved_by_date_svp:Optional[date] class RequisitionFormUpdate(BaseModel): position:Optional[Position]=None replacement_for:Optional[ReplacementFor]=None refferal_by:Optional[InternalRecommendate]=None initiated_by:Optional[str]=None initiated_date:Optional[date]=None recommended_by:Optional[str]=None recommended_date:Optional[date]=None approved_by_hr:Optional[bool]=None approved_by_date_hr:Optional[date]=None approved_by_vp:Optional[bool]=None approved_by_date_vp:Optional[date]=None approved_by_svp:Optional[bool]=None approved_by_date_svp:Optional[date]=None class FormCreate(BaseModel): form_type: str inbox_id: int | None = None manual_upload_candidate_id: str | None = None job_post_id: str | None = None interviewer_id: str | None = None form_date: datetime | None = None sections: list | None = None fields: dict | None = None recommendation: str | None = None class FormUpdate(BaseModel): interviewer_id: str | None = None form_date: datetime | None = None sections: list | None = None fields: dict | None = None recommendation: str | None = None @router.get("/forms/requisition/search") async def search_requisitions( current_user: dict = Depends( require_permission( PermissionTag.REQUISITIONS_VIEW, PermissionTag.JOB_BOARD_CREATE, PermissionTag.JOBS_CREATE, require_all=False, ) ), q: str | None = Query(None), top: int = Query(50, ge=1, le=100), job_post_id: uuid.UUID | None = Query(None), session: AsyncSession = Depends(get_session), ): """Searchable picker for job create: `{position_title} - {department}`. `q` matches either field (ilike). Empty `q` returns recent rows. Linked requisitions are omitted (1:1 with job posts). Pass `job_post_id` on edit so the job's current requisition remains selectable until unlinked. """ try: service = RequisitionForm(session=session) data = await service.search(q, top=top, job_post_id=job_post_id) return JSONResponse(content={"data": data, "status_code": 200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/forms/requisition/fetch") async def fetch_requisition_form( current_user:dict=Depends(require_permission(PermissionTag.REQUISITIONS_VIEW)), form_id:str=Query(None), session:AsyncSession=Depends(get_session), ): """Requisition table. Admins get every non-deleted row (job link does not hide anything). Other roles stay scoped to created_by.""" try: service=RequisitionForm(session=session) data=await service.get_form_by_id(form_id,current_user) return JSONResponse(content={"data":data,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/forms/requisition/create") async def create_requisition_form( payload: RequisitionFormCreate, current_user:dict=Depends(require_permission(PermissionTag.REQUISITIONS_CREATE)), session:AsyncSession=Depends(get_session), ): try: service = RequisitionForm(session=session) data = await service.create_form(payload.model_dump(exclude_unset=True), current_user) return JSONResponse(content={"data": data, "status_code": 200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.patch("/forms/requisition/update") async def update_requisition_form( payload: RequisitionFormUpdate, current_user:dict=Depends(require_permission(PermissionTag.REQUISITIONS_EDIT)), form_id:str=Query(...), session:AsyncSession=Depends(get_session), ): try: service=RequisitionForm(session=session) data=await service.update_form(form_id,payload.model_dump(exclude_unset=True),current_user) return JSONResponse(content={"data":data,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.get("/forms/definitions") async def fetch_form_definitions( current_user: dict = Depends(require_permission(PermissionTag.INTERVIEWS_VIEW)), ): try: return JSONResponse(content={"data": definitions_payload(), "status_code": 200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/forms/fetch") async def fetch_forms( current_user: dict = Depends(require_permission(PermissionTag.INTERVIEWS_VIEW)), form_id: str | None = Query(None), inbox_id: int | None = Query(None), manual_upload_candidate_id: str | None = Query(None), job_post_id: str | None = Query(None), form_type: str | None = Query(None), top: int | None = Query(None), skip: int = Query(0, ge=0), session: AsyncSession = Depends(get_session), ): try: service = CandidateForm(session=session) data, summary, total = await service.get_forms( form_id, inbox_id, manual_upload_candidate_id, job_post_id, form_type, top, skip, current_user=current_user, ) return JSONResponse( content={"data": data, "summary": summary, "total": total, "status_code": 200} ) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/forms/create") async def create_form( payload: FormCreate, current_user: dict = Depends(require_permission(PermissionTag.INTERVIEWS_CREATE)), session: AsyncSession = Depends(get_session), ): try: service = CandidateForm(session=session) data = await service.create_form(payload.model_dump(exclude_unset=True), current_user) return JSONResponse(content={"data": data, "status_code": 200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.patch("/forms/update") async def update_form( payload: FormUpdate, current_user: dict = Depends(require_permission(PermissionTag.INTERVIEWS_EDIT)), form_id: str = Query(...), session: AsyncSession = Depends(get_session), ): try: service = CandidateForm(session=session) data = await service.update_form( form_id, payload.model_dump(exclude_unset=True), current_user ) return JSONResponse(content={"data": data, "status_code": 200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.delete("/forms/delete") async def delete_form( current_user: dict = Depends(require_permission(PermissionTag.INTERVIEWS_DELETE)), form_id: str = Query(...), session: AsyncSession = Depends(get_session), ): try: service = CandidateForm(session=session) data = await service.delete_form(form_id, current_user) return JSONResponse(content={"data": data, "status_code": 200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e))