"""Requisition workflow: status transitions, milestones, summary counts."""
from fastapi import HTTPException, status as http_status
from sqlalchemy import func, select
from sqlalchemy.orm import Session

from app.core.time import range_bounds_utc, today_local, utcnow
from app.models.enums import REQUISITION_TRANSITIONS, RequisitionStatus
from app.models.requisition import Requisition, RequisitionStatusHistory
from app.models.user import User
from app.schemas.requisition import RequisitionStatusChange


def _bad_request(detail: str) -> HTTPException:
    return HTTPException(status_code=http_status.HTTP_400_BAD_REQUEST, detail=detail)


def allowed_transitions(current: RequisitionStatus) -> list[RequisitionStatus]:
    return sorted(REQUISITION_TRANSITIONS[current], key=lambda s: s.value)


def record_history(
    db: Session,
    requisition: Requisition,
    *,
    from_status: RequisitionStatus | None,
    to_status: RequisitionStatus,
    user_id: int | None,
    remarks: str | None = None,
) -> RequisitionStatusHistory:
    entry = RequisitionStatusHistory(
        requisition_id=requisition.id,
        from_status=from_status,
        to_status=to_status,
        changed_by_id=user_id,
        changed_at=utcnow(),
        remarks=remarks,
    )
    db.add(entry)
    return entry


def change_status(
    db: Session,
    requisition: Requisition,
    payload: RequisitionStatusChange,
    actor: User,
) -> None:
    """Apply a status change, enforcing the workflow. Admin-only decision actions."""
    current = requisition.status
    target = payload.status

    if target == current:
        raise _bad_request(f"Requisition is already '{current.value}'")
    if target not in REQUISITION_TRANSITIONS[current]:
        allowed = ", ".join(s.value for s in allowed_transitions(current)) or "none"
        raise _bad_request(
            f"Cannot move from '{current.value}' to '{target.value}'. Allowed: {allowed}"
        )

    remarks = (payload.remarks or "").strip() or None
    if target == RequisitionStatus.REJECTED and not remarks:
        raise _bad_request("A reason is required when rejecting a requisition")

    now = utcnow()
    if target == RequisitionStatus.APPROVED:
        requisition.approved_by_id = actor.id
        requisition.approved_at = now
    elif target == RequisitionStatus.REJECTED:
        requisition.rejected_by_id = actor.id
        requisition.rejected_at = now
    elif target == RequisitionStatus.COMPLETED:
        requisition.completed_by_id = actor.id
        requisition.completed_at = now
    elif target == RequisitionStatus.PENDING:
        # Re-opening a rejected request: clear the outcome so the record does not
        # read as both live and declined.
        requisition.rejected_by_id = None
        requisition.rejected_at = None

    requisition.status = target
    record_history(
        db,
        requisition,
        from_status=current,
        to_status=target,
        user_id=actor.id,
        remarks=remarks,
    )


def summary(db: Session) -> dict:
    """Headline counts for the module header."""
    active = Requisition.is_deleted.is_(False)
    today_start, today_end = range_bounds_utc(today_local(), today_local())

    def count(*conditions) -> int:
        return db.scalar(select(func.count()).select_from(Requisition).where(active, *conditions)) or 0

    return {
        "pending": count(Requisition.status == RequisitionStatus.PENDING),
        "approved": count(Requisition.status == RequisitionStatus.APPROVED),
        "rejected": count(Requisition.status == RequisitionStatus.REJECTED),
        "completed": count(Requisition.status == RequisitionStatus.COMPLETED),
        "requested_today": count(
            Requisition.requested_at >= today_start, Requisition.requested_at < today_end
        ),
        "total": count(),
    }
