"""Backend API for Lean verification and research-job orchestration."""

from __future__ import annotations

from decimal import Decimal
from typing import Any

from fastapi import FastAPI, HTTPException
from postgrest.exceptions import APIError
from pydantic import BaseModel, Field

from backend.lean_checker import check_conjecture
from backend.research_jobs import get_research_job, list_research_jobs, submit_research_job
from backend.research_worker import ResearchWorker
from backend.supabase_runtime import supabase_is_configured
from backend.verified_lemma_store import store_verified_lemma
from rc.credits import parse_credit_amount_to_subunits
app = FastAPI(title="Lean Research Backend")


class ConjectureCheckRequest(BaseModel):
    conjecture: str = Field(..., min_length=1)
    source_task_id: str | None = None
    source_prompt: str | None = None
    metadata: dict[str, Any] = Field(default_factory=dict)


class ConjectureCheckResponse(BaseModel):
    is_true: bool
    verification_status: str
    verification_summary: str
    normalized_code: str
    stored: bool
    document_id: str | None = None
    compiler: str
    stdout: str = ""
    stderr: str = ""
    storage_error: str | None = None


class ResearchJobCreateRequest(BaseModel):
    requester_node_id: str = Field(..., min_length=1)
    requester_secret_hash: str = Field(..., min_length=1)
    domain: str = ""
    model: str = ""
    total_budget: Decimal = Field(..., gt=0)
    budget_per_attempt: Decimal = Field(..., gt=0)
    max_concurrent_episodes: int = Field(1, gt=0)
    max_attempts_per_episode: int = Field(4, gt=0)
    seed_conjecture: str = ""
    objective: str = "autonomous_research"
    metadata: dict[str, Any] = Field(default_factory=dict)


class ResearchJobCreateResponse(BaseModel):
    job_id: str
    status: str


@app.on_event("startup")
def startup_research_worker() -> None:
    worker = ResearchWorker.from_env()
    if worker is None:
        app.state.research_worker = None
        return
    app.state.research_worker = worker
    worker.start()


@app.on_event("shutdown")
def shutdown_research_worker() -> None:
    worker = getattr(app.state, "research_worker", None)
    if worker is not None:
        worker.stop()


@app.get("/health")
def health() -> dict[str, bool]:
    return {"ok": True}


@app.get("/ready")
def ready() -> dict[str, bool]:
    configured = supabase_is_configured()
    return {
        "ok": configured,
        "supabase_configured": configured,
        "research_worker_running": getattr(app.state, "research_worker", None) is not None,
    }


def _raise_research_http_error(exc: Exception) -> None:
    if isinstance(exc, RuntimeError):
        raise HTTPException(status_code=503, detail=str(exc)) from exc
    if isinstance(exc, APIError):
        detail = getattr(exc, "message", "") or str(exc)
        raise HTTPException(status_code=502, detail=detail) from exc
    raise HTTPException(status_code=500, detail=str(exc) or exc.__class__.__name__) from exc


@app.post("/check-conjecture", response_model=ConjectureCheckResponse)
def check_generated_conjecture(payload: ConjectureCheckRequest) -> ConjectureCheckResponse:
    result = check_conjecture(payload.conjecture)

    stored = False
    document_id: str | None = None
    storage_error: str | None = None

    if result.is_true:
        try:
            document_id = store_verified_lemma(
                {
                    "source_task_id": payload.source_task_id,
                    "source_prompt": payload.source_prompt,
                    "metadata": payload.metadata,
                    "conjecture": result.conjecture,
                    "normalized_code": result.normalized_code,
                    "verification_status": result.verification_status,
                    "verification_summary": result.verification_summary,
                    "compiler": result.compiler,
                    "stdout": result.stdout,
                    "stderr": result.stderr,
                    "is_true": result.is_true,
                }
            )
            stored = True
        except Exception as exc:  # pragma: no cover - response still needs the truth value
            storage_error = str(exc)

    return ConjectureCheckResponse(
        is_true=result.is_true,
        verification_status=result.verification_status,
        verification_summary=result.verification_summary,
        normalized_code=result.normalized_code,
        stored=stored,
        document_id=document_id,
        compiler=result.compiler,
        stdout=result.stdout,
        stderr=result.stderr,
        storage_error=storage_error,
    )


@app.post("/research-jobs", response_model=ResearchJobCreateResponse)
def create_research_job(payload: ResearchJobCreateRequest) -> ResearchJobCreateResponse:
    try:
        total_budget_subunits = parse_credit_amount_to_subunits(payload.total_budget)
        budget_per_attempt_subunits = parse_credit_amount_to_subunits(payload.budget_per_attempt)
        job_id = submit_research_job(
            {
                "p_node_id": payload.requester_node_id,
                "p_node_secret_hash": payload.requester_secret_hash,
                "p_domain": payload.domain,
                "p_model": payload.model,
                "p_total_budget": total_budget_subunits,
                "p_budget_per_attempt": budget_per_attempt_subunits,
                "p_max_concurrent_episodes": payload.max_concurrent_episodes,
                "p_max_attempts_per_episode": payload.max_attempts_per_episode,
                "p_seed_conjecture": payload.seed_conjecture,
                "p_objective": payload.objective,
                "p_metadata": payload.metadata,
            }
        )
    except Exception as exc:
        _raise_research_http_error(exc)
    return ResearchJobCreateResponse(job_id=job_id, status="active")


@app.get("/research-jobs")
def get_research_jobs(
    requester_node_id: str,
    requester_secret_hash: str,
    limit: int = 20,
) -> list[dict[str, Any]]:
    try:
        return list_research_jobs(
            requester_node_id=requester_node_id,
            requester_secret_hash=requester_secret_hash,
            limit=limit,
        )
    except Exception as exc:
        _raise_research_http_error(exc)


@app.get("/research-jobs/{job_id}")
def get_research_job_detail(
    job_id: str,
    requester_node_id: str,
    requester_secret_hash: str,
) -> dict[str, Any]:
    try:
        job = get_research_job(
            job_id=job_id,
            requester_node_id=requester_node_id,
            requester_secret_hash=requester_secret_hash,
        )
    except Exception as exc:
        _raise_research_http_error(exc)
    if not job:
        return {}
    return job
