"""Decisions router: CRUD for decision processes 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.decision import Decision, DecisionStep
from app.models.user import DuniterIdentity
from app.schemas.decision import (
    DecisionAdvanceOut,
    DecisionCreate,
    DecisionOut,
    DecisionStepCreate,
    DecisionStepOut,
    DecisionUpdate,
)
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.decision_service import advance_decision, create_vote_session_for_step

router = APIRouter()


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


async def _get_decision(
    db: AsyncSession, decision_id: uuid.UUID, org_id: uuid.UUID | None = None
) -> Decision:
    """Fetch a decision by ID within the active org scope, or raise 404."""
    stmt = (
        select(Decision)
        .options(selectinload(Decision.steps))
        .where(Decision.id == decision_id)
    )
    if org_id is not None:
        stmt = stmt.where(Decision.organization_id == org_id)
    else:
        stmt = stmt.where(Decision.organization_id.is_(None))
    result = await db.execute(stmt)
    decision = result.scalar_one_or_none()
    if decision is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Decision introuvable")
    return decision


# ── Decision routes ─────────────────────────────────────────────────────────


@router.get("/", response_model=list[DecisionOut])
async def list_decisions(
    db: AsyncSession = Depends(get_db),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
    decision_type: str | None = Query(default=None, description="Filtrer par type de decision"),
    status_filter: str | None = Query(default=None, alias="status", description="Filtrer par statut"),
    skip: int = Query(default=0, ge=0),
    limit: int = Query(default=50, ge=1, le=200),
) -> list[DecisionOut]:
    """List all decisions with optional filters."""
    stmt = select(Decision).options(selectinload(Decision.steps))

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

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

    return [DecisionOut.model_validate(d) for d in decisions]


@router.post("/", response_model=DecisionOut, status_code=status.HTTP_201_CREATED)
async def create_decision(
    payload: DecisionCreate,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> DecisionOut:
    """Create a new decision process."""
    decision = Decision(
        **payload.model_dump(),
        created_by_id=identity.id,
        organization_id=org_id,
    )
    db.add(decision)
    await db.commit()
    await db.refresh(decision)

    # Reload with steps (empty at creation)
    decision = await _get_decision(db, decision.id, org_id)
    return DecisionOut.model_validate(decision)


@router.get("/{id}", response_model=DecisionOut)
async def get_decision(
    id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> DecisionOut:
    """Get a single decision with all its steps."""
    decision = await _get_decision(db, id, org_id)
    return DecisionOut.model_validate(decision)


@router.put("/{id}", response_model=DecisionOut)
async def update_decision(
    id: uuid.UUID,
    payload: DecisionUpdate,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> DecisionOut:
    """Update a decision's metadata (title, description, status, protocol)."""
    decision = await _get_decision(db, id, org_id)

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

    await db.commit()
    await db.refresh(decision)

    # Reload with steps
    decision = await _get_decision(db, decision.id, org_id)
    return DecisionOut.model_validate(decision)


# ── Decision Step routes ────────────────────────────────────────────────────


@router.post("/{id}/steps", response_model=DecisionStepOut, status_code=status.HTTP_201_CREATED)
async def add_step(
    id: uuid.UUID,
    payload: DecisionStepCreate,
    db: AsyncSession = Depends(get_db),
    identity: DuniterIdentity = Depends(get_current_identity),
    org_id: uuid.UUID | None = Depends(get_active_org_id),
) -> DecisionStepOut:
    """Add a step to a decision process."""
    # Verify decision exists
    decision = await _get_decision(db, id, org_id)

    step = DecisionStep(
        decision_id=decision.id,
        **payload.model_dump(),
    )
    db.add(step)
    await db.commit()
    await db.refresh(step)

    return DecisionStepOut.model_validate(step)


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


@router.post("/{id}/advance", response_model=DecisionAdvanceOut)
async def advance_decision_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),
) -> DecisionAdvanceOut:
    """Advance a decision to its next step or status."""
    try:
        decision = await advance_decision(id, db)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))

    # Reload with steps for complete output
    decision = await _get_decision(db, decision.id, org_id)
    data = DecisionOut.model_validate(decision).model_dump()
    data["message"] = f"Decision avancee au statut : {decision.status}"
    return DecisionAdvanceOut(**data)


@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:
    """Create a vote session linked to a decision step."""
    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)


@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT, response_class=Response, response_model=None)
async def delete_decision(
    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:
    """Delete a decision (only if in draft status)."""
    decision = await _get_decision(db, id, org_id)

    if decision.status != "draft":
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Seules les decisions en brouillon peuvent etre supprimees",
        )

    await db.delete(decision)
    await db.commit()
