from __future__ import annotations

from datetime import date as dt_date

from fastapi import APIRouter, Depends, Header, HTTPException, Query
from pydantic import BaseModel, Field
from sqlmodel import Session, select

from app.db.session import get_session
from app.models import CalendarEvent, Child, Exam, ExamStatus
from app.services.admin_auth import get_admin_session
from app.services.calendar import build_calendar, cancel_exam_study_plan, generate_study_plan, serialize_calendar_event, serialize_exam

router = APIRouter()


def require_admin(
    x_admin_session: str | None = Header(default=None, alias="X-Admin-Session"),
    session: Session = Depends(get_session),
) -> None:
    get_admin_session(session, x_admin_session)


class ExamCreate(BaseModel):
    child_id: int
    subject: str = Field(min_length=1, max_length=80)
    topic: str = Field(min_length=1, max_length=160)
    exam_date: dt_date
    study_sessions: int = Field(default=3, ge=0, le=10)
    include_weekends: bool = False


class ExamUpdate(BaseModel):
    subject: str | None = Field(default=None, min_length=1, max_length=80)
    topic: str | None = Field(default=None, min_length=1, max_length=160)
    exam_date: dt_date | None = None
    study_sessions: int | None = Field(default=None, ge=0, le=10)
    include_weekends: bool | None = None
    status: ExamStatus | None = None


class CalendarEventCreate(BaseModel):
    date: dt_date
    title: str = Field(min_length=1, max_length=160)
    description: str = Field(default="", max_length=500)
    child_id: int | None = None
    icon: str | None = Field(default=None, max_length=16)
    color: str | None = Field(default=None, max_length=32)


class CalendarEventUpdate(BaseModel):
    date: dt_date | None = None
    title: str | None = Field(default=None, min_length=1, max_length=160)
    description: str | None = Field(default=None, max_length=500)
    child_id: int | None = None
    icon: str | None = Field(default=None, max_length=16)
    color: str | None = Field(default=None, max_length=32)
    active: bool | None = None


@router.get("/calendar")
def get_calendar(
    start: dt_date = Query(...),
    weeks: int = Query(default=3, ge=1, le=8),
    session: Session = Depends(get_session),
):
    return build_calendar(session, start, weeks)


@router.post("/admin/calendar-events", status_code=201)
def create_calendar_event(payload: CalendarEventCreate, _: None = Depends(require_admin), session: Session = Depends(get_session)):
    if payload.child_id is not None and session.get(Child, payload.child_id) is None:
        raise HTTPException(status_code=404, detail={"error": {"code": "child_not_found", "message": "Kind nicht gefunden"}})
    event = CalendarEvent(day=payload.date, title=payload.title, description=payload.description, child_id=payload.child_id, icon=payload.icon, color=payload.color, active=True)
    session.add(event)
    session.commit()
    session.refresh(event)
    child = session.get(Child, event.child_id) if event.child_id else None
    return {"calendar_event": serialize_calendar_event(event, child)}


@router.patch("/admin/calendar-events/{event_id}")
def update_calendar_event(event_id: int, payload: CalendarEventUpdate, _: None = Depends(require_admin), session: Session = Depends(get_session)):
    event = session.get(CalendarEvent, event_id)
    if event is None:
        raise HTTPException(status_code=404, detail={"error": {"code": "not_found", "message": "Termin nicht gefunden"}})
    data = payload.model_dump(exclude_unset=True)
    if "date" in data:
        event.day = data.pop("date")
    for key, value in data.items():
        setattr(event, key, value)
    session.add(event)
    session.commit()
    session.refresh(event)
    child = session.get(Child, event.child_id) if event.child_id else None
    return {"calendar_event": serialize_calendar_event(event, child)}


@router.delete("/admin/calendar-events/{event_id}")
def delete_calendar_event(event_id: int, _: None = Depends(require_admin), session: Session = Depends(get_session)):
    event = session.get(CalendarEvent, event_id)
    if event is None:
        raise HTTPException(status_code=404, detail={"error": {"code": "not_found", "message": "Termin nicht gefunden"}})
    event.active = False
    session.add(event)
    session.commit()
    session.refresh(event)
    child = session.get(Child, event.child_id) if event.child_id else None
    return {"ok": True, "calendar_event": serialize_calendar_event(event, child)}


@router.get("/admin/exams")
def list_exams(_: None = Depends(require_admin), session: Session = Depends(get_session)):
    children = session.exec(select(Child)).all()
    child_lookup = {child.id: child for child in children}
    exams = session.exec(select(Exam).where(Exam.status != ExamStatus.cancelled).order_by(Exam.exam_date)).all()
    return {"exams": [serialize_exam(exam, child_lookup.get(exam.child_id)) for exam in exams]}


@router.post("/admin/exams", status_code=201)
def create_exam(payload: ExamCreate, _: None = Depends(require_admin), session: Session = Depends(get_session)):
    exam = Exam(**payload.model_dump())
    session.add(exam)
    session.flush()
    generate_study_plan(session, exam)
    session.commit()
    session.refresh(exam)
    return {"exam": serialize_exam(exam, None)}


@router.patch("/admin/exams/{exam_id}")
def update_exam(exam_id: int, payload: ExamUpdate, _: None = Depends(require_admin), session: Session = Depends(get_session)):
    exam = session.get(Exam, exam_id)
    if exam is None:
        raise HTTPException(status_code=404, detail={"error": {"code": "not_found", "message": "Prüfung nicht gefunden"}})

    previous_status = exam.status
    data = payload.model_dump(exclude_unset=True)
    for key, value in data.items():
        setattr(exam, key, value)

    if exam.status == ExamStatus.cancelled:
        cancel_exam_study_plan(session, exam)
    elif previous_status == ExamStatus.cancelled or any(key in data for key in ("exam_date", "study_sessions", "include_weekends")):
        generate_study_plan(session, exam)

    session.commit()
    session.refresh(exam)
    return {"exam": serialize_exam(exam, None)}


@router.post("/admin/exams/{exam_id}/cancel")
def cancel_exam(exam_id: int, _: None = Depends(require_admin), session: Session = Depends(get_session)):
    exam = session.get(Exam, exam_id)
    if exam is None:
        raise HTTPException(status_code=404, detail={"error": {"code": "not_found", "message": "Prüfung nicht gefunden"}})
    exam.status = ExamStatus.cancelled
    cancel_exam_study_plan(session, exam)
    session.commit()
    session.refresh(exam)
    return {"exam": serialize_exam(exam, None)}


@router.delete("/admin/exams/{exam_id}")
def delete_exam(exam_id: int, _: None = Depends(require_admin), session: Session = Depends(get_session)):
    return cancel_exam(exam_id, _, session)
