"""Booking template endpoints.""" from datetime import datetime, timedelta from typing import Annotated from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from sqlalchemy.orm import Session, joinedload from app.core.deps import get_current_user, get_db from app.models.booking import Booking from app.models.booking_template import BookingTemplate from app.models.user import User from app.schemas.booking import BookingResponse from app.schemas.booking_template import BookingTemplateCreate, BookingTemplateRead from app.services.booking_service import validate_booking_rules from app.services.email_service import send_booking_notification from app.services.notification_service import create_notification router = APIRouter(prefix="/booking-templates", tags=["booking-templates"]) @router.post("", response_model=BookingTemplateRead, status_code=status.HTTP_201_CREATED) def create_template( data: BookingTemplateCreate, db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_user)], ) -> BookingTemplateRead: """ Create a new booking template. - **name**: Template name (e.g., "Weekly Team Sync") - **space_id**: Optional default space ID - **duration_minutes**: Default duration in minutes - **title**: Default booking title - **description**: Optional default description Returns the created template. """ template = BookingTemplate( user_id=current_user.id, # type: ignore[arg-type] name=data.name, space_id=data.space_id, duration_minutes=data.duration_minutes, title=data.title, description=data.description, ) db.add(template) db.commit() db.refresh(template) return BookingTemplateRead( id=template.id, # type: ignore[arg-type] user_id=template.user_id, # type: ignore[arg-type] name=template.name, # type: ignore[arg-type] space_id=template.space_id, # type: ignore[arg-type] space_name=template.space.name if template.space else None, # type: ignore[union-attr] duration_minutes=template.duration_minutes, # type: ignore[arg-type] title=template.title, # type: ignore[arg-type] description=template.description, # type: ignore[arg-type] usage_count=template.usage_count, # type: ignore[arg-type] ) @router.get("", response_model=list[BookingTemplateRead]) def list_templates( db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_user)], ) -> list[BookingTemplateRead]: """ List all booking templates for the current user. Returns templates sorted by name. """ templates = ( db.query(BookingTemplate) .options(joinedload(BookingTemplate.space)) .filter(BookingTemplate.user_id == current_user.id) .order_by(BookingTemplate.name) .all() ) return [ BookingTemplateRead( id=t.id, # type: ignore[arg-type] user_id=t.user_id, # type: ignore[arg-type] name=t.name, # type: ignore[arg-type] space_id=t.space_id, # type: ignore[arg-type] space_name=t.space.name if t.space else None, # type: ignore[union-attr] duration_minutes=t.duration_minutes, # type: ignore[arg-type] title=t.title, # type: ignore[arg-type] description=t.description, # type: ignore[arg-type] usage_count=t.usage_count, # type: ignore[arg-type] ) for t in templates ] @router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT) def delete_template( id: int, db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_user)], ) -> None: """ Delete a booking template. Users can only delete their own templates. """ template = ( db.query(BookingTemplate) .filter( BookingTemplate.id == id, BookingTemplate.user_id == current_user.id, ) .first() ) if not template: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Template not found", ) db.delete(template) db.commit() return None @router.post("/from-template/{template_id}", response_model=BookingResponse, status_code=status.HTTP_201_CREATED) def create_booking_from_template( template_id: int, start_datetime: datetime, background_tasks: BackgroundTasks, db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_user)], ) -> Booking: """ Create a booking from a template. - **template_id**: ID of the template to use - **start_datetime**: When the booking should start (ISO format) The booking will use the template's space, title, description, and duration. The end time is calculated automatically based on the template's duration. Returns the created booking with status "pending" (requires admin approval). """ # Find template template = ( db.query(BookingTemplate) .filter( BookingTemplate.id == template_id, BookingTemplate.user_id == current_user.id, ) .first() ) if not template: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Template not found", ) if not template.space_id: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Template does not have a default space", ) # Calculate end time end_datetime = start_datetime + timedelta(minutes=template.duration_minutes) # type: ignore[arg-type] # Validate booking rules user_id = int(current_user.id) # type: ignore[arg-type] errors = validate_booking_rules( db=db, space_id=int(template.space_id), # type: ignore[arg-type] start_datetime=start_datetime, end_datetime=end_datetime, user_id=user_id, ) if errors: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=errors[0], # Return first error ) # Create booking booking = Booking( user_id=user_id, space_id=template.space_id, # type: ignore[arg-type] title=template.title, # type: ignore[arg-type] description=template.description, # type: ignore[arg-type] start_datetime=start_datetime, end_datetime=end_datetime, status="pending", created_at=datetime.utcnow(), ) db.add(booking) # Increment usage count template.usage_count = int(template.usage_count) + 1 # type: ignore[arg-type, assignment] db.commit() db.refresh(booking) # Notify all admins about the new booking request admins = db.query(User).filter(User.role == "admin").all() for admin in admins: create_notification( db=db, user_id=admin.id, # type: ignore[arg-type] type="booking_created", title="Noua Cerere de Rezervare", message=f"Utilizatorul {current_user.full_name} a solicitat rezervarea spațiului {template.space.name} pentru {booking.start_datetime.strftime('%d.%m.%Y %H:%M')}", # type: ignore[union-attr, union-attr] booking_id=booking.id, ) # Send email notification to admin background_tasks.add_task( send_booking_notification, booking, "created", admin.email, current_user.full_name, None, ) return booking