"""Mandates router: CRUD for mandates and their steps."""

from __future__ import annotations

import uuid

from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.database import get_db
from app.models.mandate import Mandate, MandateStep
from app.models.user import DuniterIdentity
from app.schemas.mandate import (
    MandateAdvanceOut,
    MandateAssignRequest,
    MandateCreate,
    MandateOut,
    MandateStepCreate,
    MandateStepOut,
    MandateUpdate,
)
from app.schemas.vote import VoteSessionOut
from app.dependencies.org import get_active_org_id
from app.services.auth_service import get_current_identity
from app.services.mandate_service import (
    advance_mandate,
    assign_mandatee,
    create_vote_session_for_step,
    revoke_mandate,
)

router = APIRouter()


# ── Helpers ─────────────────────────────────────────────────────────────────


async def _get_mandate(
    db: AsyncSession, mandate_id: uuid.UUID, org_id: uuid.UUID | None = None
) -> Mandate:
    stmt = (
        select(Mandate)
        .options(
            selectinload(Mandate.steps),
            selectinload(Mandate.origin_identity),
            selectinload(Mandate.mandatee),
        )
        .where(Mandate.id == mandate_id)
    )
    if org_id is not None:
        stmt = stmt.where(Mandate.organization_id == org_id)
    else:
        stmt = stmt.where(Mandate.organization_id.is_(None))
    result = await db.execute(stmt)
    mandate = result.scalar_one_or_none()
    if mandate is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Mandat introuvable")
    return mandate


def _mandate_out(mandate: Mandate) -> MandateOut:
    out = MandateOut.model_validate(mandate)
    out.origin_display_name = mandate.origin_display_name
    out.mandatee_display_name = mandate.mandatee_display_name
    return out


# ── Mandate routes ──────────────────────────────────────────────────────────


@router.get("/", response_model=list[MandateOut])
async def list_mandates(
    db: AsyncSession = Depends(get_db),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
    mandate_type: str | None = Query(default=None),
    status_filter: str | None = Query(default=None, alias="status"),
    skip: int = Query(default=0, ge=0),
    limit: int = Query(default=50, ge=1, le=200),
) -> list[MandateOut]:
    stmt = select(Mandate).options(
        selectinload(Mandate.steps),
        selectinload(Mandate.origin_identity),
        selectinload(Mandate.mandatee),
    )

    if org_id is not None:
        stmt = stmt.where(Mandate.organization_id == org_id)
    else:
        stmt = stmt.where(Mandate.organization_id.is_(None))
    if mandate_type is not None:
        stmt = stmt.where(Mandate.mandate_type == mandate_type)
    if status_filter is not None:
        stmt = stmt.where(Mandate.status == status_filter)

    stmt = stmt.order_by(Mandate.created_at.desc()).offset(skip).limit(limit)
    result = await db.execute(stmt)
    mandates = result.scalars().unique().all()

    return [_mandate_out(m) for m in mandates]


@router.post("/", response_model=MandateOut, status_code=status.HTTP_201_CREATED)
async def create_mandate(
    payload: MandateCreate,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> MandateOut:
    data = payload.model_dump()
    nomination_mode = data.pop("nomination_mode", "postpone")

    mandate = Mandate(**data, organization_id=org_id)

    if nomination_mode == "auto":
        mandate.mandatee_id = identity.id

    db.add(mandate)
    await db.commit()
    await db.refresh(mandate)

    mandate = await _get_mandate(db, mandate.id, org_id)
    return _mandate_out(mandate)


@router.get("/{id}", response_model=MandateOut)
async def get_mandate(
    id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> MandateOut:
    mandate = await _get_mandate(db, id, org_id)
    return _mandate_out(mandate)


@router.put("/{id}", response_model=MandateOut)
async def update_mandate(
    id: uuid.UUID,
    payload: MandateUpdate,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> MandateOut:
    mandate = await _get_mandate(db, id, org_id)

    for field, value in payload.model_dump(exclude_unset=True).items():
        setattr(mandate, field, value)

    await db.commit()
    mandate = await _get_mandate(db, mandate.id, org_id)
    return _mandate_out(mandate)


@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT, response_class=Response, response_model=None)
async def delete_mandate(
    id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> None:
    mandate = await _get_mandate(db, id, org_id)

    if mandate.status != "draft":
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Seuls les mandats en brouillon peuvent etre supprimes",
        )

    await db.delete(mandate)
    await db.commit()


# ── Mandate Step routes ─────────────────────────────────────────────────────


@router.post("/{id}/steps", response_model=MandateStepOut, status_code=status.HTTP_201_CREATED)
async def add_step(
    id: uuid.UUID,
    payload: MandateStepCreate,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> MandateStepOut:
    mandate = await _get_mandate(db, id, org_id)

    step = MandateStep(mandate_id=mandate.id, **payload.model_dump())
    db.add(step)
    await db.commit()
    await db.refresh(step)

    return MandateStepOut.model_validate(step)


@router.get("/{id}/steps", response_model=list[MandateStepOut])
async def list_steps(
    id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> list[MandateStepOut]:
    mandate = await _get_mandate(db, id, org_id)
    return [MandateStepOut.model_validate(s) for s in mandate.steps]


# ── Workflow routes ────────────────────────────────────────────────────────


@router.post("/{id}/advance", response_model=MandateAdvanceOut)
async def advance_mandate_endpoint(
    id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> MandateAdvanceOut:
    try:
        mandate = await advance_mandate(id, db)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))

    mandate = await _get_mandate(db, mandate.id, org_id)
    out = _mandate_out(mandate)
    return MandateAdvanceOut(
        **out.model_dump(),
        message=f"Mandat avance au statut : {mandate.status}",
    )


@router.post("/{id}/assign", response_model=MandateOut)
async def assign_mandatee_endpoint(
    id: uuid.UUID,
    payload: MandateAssignRequest,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> MandateOut:
    try:
        mandate = await assign_mandatee(id, payload.mandatee_id, db)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))

    mandate = await _get_mandate(db, mandate.id, org_id)
    return _mandate_out(mandate)


@router.post("/{id}/revoke", response_model=MandateOut)
async def revoke_mandate_endpoint(
    id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> MandateOut:
    try:
        mandate = await revoke_mandate(id, db)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))

    mandate = await _get_mandate(db, mandate.id, org_id)
    return _mandate_out(mandate)


@router.post(
    "/{id}/steps/{step_id}/create-vote-session",
    response_model=VoteSessionOut,
    status_code=status.HTTP_201_CREATED,
)
async def create_vote_session_for_step_endpoint(
    id: uuid.UUID,
    step_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> VoteSessionOut:
    try:
        session = await create_vote_session_for_step(id, step_id, db)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))

    return VoteSessionOut.model_validate(session)
