"""
Runbook runs API — create run, get run, next step, complete step, abort (PR2).
Evidence + post-review generation (PR4).
All under require_auth.
"""
from __future__ import annotations

from typing import Any, Dict, Optional

from fastapi import APIRouter, Depends, HTTPException, Body

from .auth import require_auth
from . import runbook_runner as runner
from . import runbook_artifacts as artifacts

runbook_runs_router = APIRouter(prefix="/api/runbooks/runs", tags=["runbook-runs"])


@runbook_runs_router.post("")
async def create_run(
    body: Dict[str, Any] = Body(...),
    _auth: str = Depends(require_auth),
):
    """Create a new run from runbook_path. Returns run_id, status, current_step, steps_total."""
    runbook_path = (body.get("runbook_path") or "").strip()
    if not runbook_path or ".." in runbook_path:
        raise HTTPException(status_code=400, detail="Invalid runbook_path")
    try:
        out = await runner.create_run(
            runbook_path=runbook_path,
            operator_id=(body.get("operator_id") or "").strip() or None,
            node_id=(body.get("node_id") or "").strip() or None,
            sofiia_url=(body.get("sofiia_url") or "").strip() or None,
            data_json=body.get("data"),
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    return out


@runbook_runs_router.get("/{run_id}")
async def get_run(
    run_id: str,
    _auth: str = Depends(require_auth),
):
    """Get run and steps (light)."""
    run = await runner.get_run(run_id)
    if not run:
        raise HTTPException(status_code=404, detail="Run not found")
    return run


@runbook_runs_router.post("/{run_id}/next")
async def next_step(
    run_id: str,
    body: Dict[str, Any] = Body(default=None),
    _auth: str = Depends(require_auth),
):
    """Execute next step: if http_check run it and return result; if manual return instructions."""
    operator_id = (body or {}).get("operator_id") or ""
    out = await runner.next_step(run_id, operator_id=operator_id)
    if out is None:
        raise HTTPException(status_code=404, detail="Run not found or not active")
    return out


@runbook_runs_router.post("/{run_id}/steps/{step_index}/complete")
async def complete_step(
    run_id: str,
    step_index: int,
    body: Dict[str, Any] = Body(...),
    _auth: str = Depends(require_auth),
):
    """Mark manual step complete. Body: status (ok|warn|fail|skipped), notes, data."""
    status = (body.get("status") or "ok").strip()
    notes = (body.get("notes") or "").strip()
    data = body.get("data")
    operator_id = (body.get("operator_id") or "").strip()
    ok = await runner.complete_step(run_id, step_index, status=status, notes=notes, data=data, operator_id=operator_id)
    if not ok:
        raise HTTPException(status_code=404, detail="Run/step not found or step not current")
    return {"ok": True, "run_id": run_id, "step_index": step_index, "next_step": step_index + 1}


@runbook_runs_router.get("/{run_id}/artifacts")
async def list_artifacts(
    run_id: str,
    _auth: str = Depends(require_auth),
):
    """List generated artifacts for a run (paths, sizes, mtimes)."""
    return await artifacts.list_run_artifacts(run_id)


@runbook_runs_router.post("/{run_id}/evidence")
async def generate_evidence(
    run_id: str,
    _auth: str = Depends(require_auth),
):
    """Generate release evidence markdown from run step results. Returns {evidence_path, bytes, created_at}."""
    try:
        out = await artifacts.render_release_evidence(run_id)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    return out


@runbook_runs_router.post("/{run_id}/post_review")
async def generate_post_review(
    run_id: str,
    _auth: str = Depends(require_auth),
):
    """Generate post-release review markdown. Returns {path, bytes, created_at}."""
    try:
        out = await artifacts.render_post_review(run_id)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    return out


@runbook_runs_router.post("/{run_id}/abort")
async def abort_run(
    run_id: str,
    body: Dict[str, Any] = Body(default=None),
    _auth: str = Depends(require_auth),
):
    """Abort run."""
    operator_id = (body or {}).get("operator_id") or ""
    ok = await runner.abort_run(run_id, operator_id=operator_id)
    if not ok:
        raise HTTPException(status_code=404, detail="Run not found")
    return {"ok": True, "run_id": run_id, "status": "aborted"}
