"""Appointment booking API.""" from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.db import get_db from app.models import Appointment, Conversation, Message from app.schemas import AppointmentResponse, BookAppointmentRequest from app.services import calcom router = APIRouter(prefix="/api/appointments", tags=["appointments"]) @router.get("/slots") async def list_slots() -> dict: """List next available slots.""" slots = await calcom.get_available_slots(3) return {"slots": slots} @router.post("/book", response_model=AppointmentResponse) async def book_appointment( body: BookAppointmentRequest, db: AsyncSession = Depends(get_db), ) -> AppointmentResponse: """Book a selected slot for a conversation.""" result = await db.execute( select(Conversation) .where(Conversation.id == body.conversation_id) .options( selectinload(Conversation.lead), selectinload(Conversation.appointment), ) ) conv = result.scalar_one_or_none() if conv is None: raise HTTPException(status_code=404, detail="Not found") if conv.appointment is not None: raise HTTPException( status_code=400, detail="Already booked" ) name = body.attendee_name email = body.attendee_email if conv.lead: name = name or conv.lead.name email = email or conv.lead.email booked = await calcom.book_slot( slot_start=body.slot_start, slot_end=body.slot_end, attendee_name=name, attendee_email=email, ) appt = Appointment( conversation_id=conv.id, slot_start=booked["slot_start"], slot_end=booked["slot_end"], attendee_name=name, attendee_email=email, external_id=booked.get("external_id"), status=booked.get("status", "confirmed"), ) conv.appointment_booked = True db.add(appt) confirm = { "en": ( f"Your meeting is confirmed for " f"{body.slot_start}. See you then!" ), "fr": ( f"Votre rendez-vous est confirmé pour " f"{body.slot_start}. À bientôt !" ), "ru": ( f"Встреча подтверждена на {body.slot_start}. " f"До связи!" ), } lang = conv.language if conv.language in confirm else "en" message = confirm[lang] db.add( Message( conversation_id=conv.id, role="assistant", content=message, language=lang, grounded=True, ) ) await db.commit() await db.refresh(appt) return AppointmentResponse( id=appt.id, slot_start=appt.slot_start, slot_end=appt.slot_end, status=appt.status, message=message, )