from __future__ import annotations

from typing import Any, Dict, NoReturn, Optional

from fastapi import HTTPException


def agent_error_detail(
    *,
    code: str,
    message: str,
    retryable: bool = False,
    next_action: Optional[str] = None,
    **extra: Any,
) -> Dict[str, Any]:
    detail: Dict[str, Any] = {
        "code": code,
        "message": message,
        "retryable": retryable,
    }
    if next_action:
        detail["next_action"] = next_action
    for key, value in extra.items():
        if value is not None:
            detail[key] = value
    return detail


def raise_agent_error(
    *,
    status_code: int,
    code: str,
    message: str,
    retryable: bool = False,
    next_action: Optional[str] = None,
    **extra: Any,
) -> NoReturn:
    raise HTTPException(
        status_code=status_code,
        detail=agent_error_detail(
            code=code,
            message=message,
            retryable=retryable,
            next_action=next_action,
            **extra,
        ),
    )


def raise_job_not_found(job_id: str) -> NoReturn:
    raise HTTPException(
        status_code=404,
        detail=agent_error_detail(
            code="job_not_found",
            message=f"Job '{job_id}' was not found.",
            retryable=False,
            next_action="Check the job_id from your /generate response. Jobs expire when the process restarts.",
            job_id=job_id,
        ),
    )


def guard_agent_write(
    *,
    donor_id: Optional[str] = None,
    job_id: Optional[str] = None,
) -> None:
    """Raise a structured 400 if a required agent write field is empty."""
    if donor_id is not None and not str(donor_id).strip():
        raise_agent_error(
            status_code=400,
            code="missing_donor_id",
            message="donor_id is required.",
            retryable=False,
            next_action="Set donor_id to a supported donor such as 'usaid', then retry the request.",
        )
    if job_id is not None and not str(job_id).strip():
        raise_agent_error(
            status_code=400,
            code="missing_job_id",
            message="job_id is required.",
            retryable=False,
            next_action="Provide a valid job_id from a previous /generate response.",
        )
