"""Langfuse control, OpenAPI, prompts, datasets, scores, annotations, agent state endpoints."""
from __future__ import annotations

from typing import Any

from fastapi import APIRouter, HTTPException, Query, Request

from api.deps import (
    _is_langfuse_mutation_operation,
    _openapi_execute_requires_write,
    _record_audit,
    _require_role,
    _require_scope,
    _run_langfuse_message_for_request,
    _run_langfuse_operation_for_request,
)
from api.models import (
    AnnotationCreateRequest,
    DatasetCreateRequest,
    DatasetImportFromTracesRequest,
    DatasetItemsUpsertRequest,
    LangfuseControlMessageRequest,
    LangfuseControlRequest,
    OpenApiExecuteRequest,
    PromptPatchRequest,
    PromptPromoteLabelRequest,
    PromptUpsertRequest,
    ScoreCreateRequest,
)
from src.agent_core import (
    get_agent_state_runtime_summary,
    get_agent_tool_presets,
    get_agent_tool_runtime_summary,
    list_agent_state_checkpoints,
)
from src.langfuse_control import LangfuseControlError
from src.tools import (
    get_langfuse_builtin_tool_registry,
    get_langfuse_openapi_tool_registry,
    is_langfuse_openapi_mutating,
    parse_langfuse_control_message,
)

router = APIRouter()


# ---------------------------------------------------------------------------
# Langfuse control
# ---------------------------------------------------------------------------

@router.get("/api/langfuse/control/status")
def langfuse_control_status(request: Request) -> dict[str, Any]:
    _require_role(request, "analyst")
    _require_scope(request, "langfuse:read")
    try:
        result = _run_langfuse_operation_for_request(request, "status")
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse control status failed: {exc}")
    return {"ok": True, "data": result}


@router.post("/api/langfuse/control/execute")
def langfuse_control_execute(payload: LangfuseControlRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "analyst")
    operation_name = str(payload.operation or "").strip().lower()
    if operation_name == "openapi.execute":
        params = payload.params if isinstance(payload.params, dict) else {}
        try:
            is_mutating = is_langfuse_openapi_mutating(
                operation_id=str(params.get("operation_id") or "").strip() or None,
                method=str(params.get("method") or "").strip() or None,
                path=str(params.get("path") or "").strip() or None,
            )
        except Exception:
            is_mutating = True
        required_scope = "langfuse:write" if is_mutating else "langfuse:read"
    else:
        required_scope = "langfuse:write" if _is_langfuse_mutation_operation(payload.operation) else "langfuse:read"
    _require_scope(request, required_scope)
    try:
        result = _run_langfuse_operation_for_request(request, payload.operation, payload.params)
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse control execution failed: {exc}")

    _record_audit(
        request,
        action="langfuse.control.execute",
        resource_type="langfuse",
        resource_id=payload.operation,
        detail={"operation": payload.operation},
    )
    return {"ok": True, "data": result}


@router.post("/api/langfuse/control/message")
def langfuse_control_message(payload: LangfuseControlMessageRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "analyst")
    try:
        command = parse_langfuse_control_message(payload.message)
        operation_name = str(command.get("operation", "")).strip().lower()
        if operation_name == "openapi.execute":
            params = command.get("params")
            if not isinstance(params, dict):
                params = {}
            try:
                is_mutating = is_langfuse_openapi_mutating(
                    operation_id=str(params.get("operation_id") or "").strip() or None,
                    method=str(params.get("method") or "").strip() or None,
                    path=str(params.get("path") or "").strip() or None,
                )
            except Exception:
                is_mutating = True
            required_scope = "langfuse:write" if is_mutating else "langfuse:read"
        else:
            required_scope = "langfuse:write" if _is_langfuse_mutation_operation(command.get("operation", "")) else "langfuse:read"
        _require_scope(request, required_scope)
        result = _run_langfuse_message_for_request(request, payload.message)
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse control message failed: {exc}")

    _record_audit(
        request,
        action="langfuse.control.message",
        resource_type="langfuse",
        resource_id=str(command.get("operation", "status")),
        detail={"operation": command.get("operation", "status")},
    )
    return {"ok": True, "data": result}


# ---------------------------------------------------------------------------
# OpenAPI
# ---------------------------------------------------------------------------

@router.get("/api/langfuse/openapi/spec")
def langfuse_openapi_spec(request: Request, full: bool = False, refresh: bool = False) -> dict[str, Any]:
    _require_role(request, "analyst")
    _require_scope(request, "langfuse:read")
    try:
        result = _run_langfuse_operation_for_request(request, "openapi.spec", {"full": full, "refresh": refresh})
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse openapi spec failed: {exc}")
    return {"ok": True, "data": result}


@router.get("/api/langfuse/openapi/operations")
def langfuse_openapi_operations(request: Request, refresh: bool = False) -> dict[str, Any]:
    _require_role(request, "analyst")
    _require_scope(request, "langfuse:read")
    try:
        result = _run_langfuse_operation_for_request(request, "openapi.list_operations", {"refresh": refresh})
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse openapi operations failed: {exc}")
    return {"ok": True, "data": result}


@router.post("/api/langfuse/openapi/execute")
def langfuse_openapi_execute(payload: OpenApiExecuteRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "analyst")
    required_scope = "langfuse:write" if _openapi_execute_requires_write(payload) else "langfuse:read"
    _require_scope(request, required_scope)
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "openapi.execute",
            {
                "operation_id": payload.operation_id,
                "method": payload.method,
                "path": payload.path,
                "path_params": payload.path_params,
                "query": payload.query,
                "body": payload.body,
                "refresh_spec": payload.refresh_spec,
            },
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse openapi execute failed: {exc}")

    _record_audit(
        request,
        action="langfuse.openapi.execute",
        resource_type="langfuse",
        resource_id=str(payload.operation_id or f"{payload.method}:{payload.path}"),
        detail={
            "operation_id": payload.operation_id,
            "method": payload.method,
            "path": payload.path,
        },
    )
    return {"ok": True, "data": result}


@router.get("/api/langfuse/openapi/tool-registry")
def langfuse_openapi_tool_registry(
    request: Request,
    refresh: bool = False,
    include_mutating: bool | None = Query(default=None),
    max_tools: int | None = Query(default=None, ge=1, le=2000),
) -> dict[str, Any]:
    _require_role(request, "analyst")
    _require_scope(request, "langfuse:read")
    try:
        data = get_langfuse_openapi_tool_registry(
            refresh=refresh,
            include_mutating=include_mutating,
            max_tools=max_tools,
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse openapi tool registry failed: {exc}")
    return {"ok": True, "data": data}


# ---------------------------------------------------------------------------
# Tools catalog / presets
# ---------------------------------------------------------------------------

@router.get("/api/langfuse/tools/catalog")
def langfuse_tools_catalog(request: Request) -> dict[str, Any]:
    _require_role(request, "analyst")
    _require_scope(request, "langfuse:read")
    try:
        runtime = get_agent_tool_runtime_summary()
        builtin_all = get_langfuse_builtin_tool_registry(include_mutating=True)
        builtin_readonly = get_langfuse_builtin_tool_registry(include_mutating=False)
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse tools catalog failed: {exc}")
    return {
        "ok": True,
        "data": {
            "runtime": runtime,
            "presets": get_agent_tool_presets(),
            "builtin": builtin_all,
            "builtin_readonly": builtin_readonly,
        },
    }


@router.get("/api/langfuse/tools/presets")
def langfuse_tool_presets(request: Request) -> dict[str, Any]:
    _require_role(request, "analyst")
    _require_scope(request, "langfuse:read")
    try:
        data = get_agent_tool_presets()
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse tool presets failed: {exc}")
    return {"ok": True, "data": data}


# ---------------------------------------------------------------------------
# Agent state
# ---------------------------------------------------------------------------

@router.get("/api/agent/state/status")
def agent_state_status(request: Request) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "system:read")
    try:
        data = get_agent_state_runtime_summary()
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"agent state status failed: {exc}")
    return {"ok": True, "data": data}


@router.get("/api/agent/state/threads/{thread_id}/checkpoints")
def agent_state_thread_checkpoints(
    thread_id: str,
    request: Request,
    limit: int = Query(default=20, ge=1, le=200),
) -> dict[str, Any]:
    _require_role(request, "analyst")
    _require_scope(request, "system:read")
    try:
        data = list_agent_state_checkpoints(thread_id=thread_id, limit=limit)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except RuntimeError as exc:
        raise HTTPException(status_code=503, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"agent state checkpoints failed: {exc}")
    return {"ok": True, "data": data}


# ---------------------------------------------------------------------------
# Prompts
# ---------------------------------------------------------------------------

@router.get("/api/langfuse/prompts")
def list_or_get_langfuse_prompts(
    request: Request,
    name: str | None = None,
    label: str | None = None,
    version: int | None = None,
    page: int | None = None,
    limit: int | None = None,
) -> dict[str, Any]:
    _require_role(request, "analyst")
    _require_scope(request, "langfuse:read")
    command: dict[str, Any]
    if name:
        command = {"operation": "prompt.get", "params": {"name": name, "label": label, "version": version}}
    else:
        params: dict[str, Any] = {}
        if label:
            params["label"] = label
        if page is not None:
            params["page"] = page
        if limit is not None:
            params["limit"] = limit
        command = {"operation": "prompt.list", "params": params}
    try:
        result = _run_langfuse_operation_for_request(
            request,
            operation=command["operation"],
            params=command.get("params") if isinstance(command.get("params"), dict) else {},
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse prompts query failed: {exc}")
    return {"ok": True, "data": result}


@router.post("/api/langfuse/prompts")
def create_or_upsert_langfuse_prompt(payload: PromptUpsertRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "langfuse:write")
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "prompt.upsert",
            {
                "name": payload.name,
                "prompt": payload.prompt,
                "type": payload.type,
                "labels": payload.labels,
                "tags": payload.tags,
                "commit_message": payload.commit_message,
                "config": payload.config,
            },
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse prompt upsert failed: {exc}")

    _record_audit(
        request,
        action="langfuse.prompt.upsert",
        resource_type="langfuse_prompt",
        resource_id=payload.name,
    )
    return {"ok": True, "data": result}


@router.patch("/api/langfuse/prompts/{name}")
def patch_langfuse_prompt(name: str, payload: PromptPatchRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "langfuse:write")
    if payload.prompt is None:
        raise HTTPException(status_code=400, detail="prompt is required for patch")
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "prompt.upsert",
            {
                "name": name,
                "prompt": payload.prompt,
                "type": payload.type,
                "labels": payload.labels if payload.labels is not None else [],
                "tags": payload.tags if payload.tags is not None else [],
                "commit_message": payload.commit_message,
                "config": payload.config if payload.config is not None else {},
            },
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse prompt patch failed: {exc}")

    _record_audit(
        request,
        action="langfuse.prompt.patch",
        resource_type="langfuse_prompt",
        resource_id=name,
    )
    return {"ok": True, "data": result}


@router.delete("/api/langfuse/prompts/{name}")
def delete_langfuse_prompt(
    name: str,
    request: Request,
    version: int | None = None,
    label: str | None = None,
) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "langfuse:write")
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "prompt.delete",
            {"name": name, "version": version, "label": label},
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse prompt delete failed: {exc}")

    _record_audit(
        request,
        action="langfuse.prompt.delete",
        resource_type="langfuse_prompt",
        resource_id=name,
    )
    return {"ok": True, "data": result}


@router.post("/api/langfuse/prompts/{name}/promote-label")
def promote_langfuse_prompt_label(name: str, payload: PromptPromoteLabelRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "langfuse:write")
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "prompt.promote_label",
            {"name": name, "label": payload.label, "version": payload.version},
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse prompt promote failed: {exc}")

    _record_audit(
        request,
        action="langfuse.prompt.promote_label",
        resource_type="langfuse_prompt",
        resource_id=name,
        detail={"label": payload.label, "version": payload.version},
    )
    return {"ok": True, "data": result}


# ---------------------------------------------------------------------------
# Datasets
# ---------------------------------------------------------------------------

@router.get("/api/langfuse/datasets")
def list_langfuse_datasets(
    request: Request,
    page: int | None = None,
    limit: int | None = None,
    name: str | None = None,
) -> dict[str, Any]:
    _require_role(request, "analyst")
    _require_scope(request, "langfuse:read")
    params: dict[str, Any] = {}
    if page is not None:
        params["page"] = page
    if limit is not None:
        params["limit"] = limit
    if name:
        params["name"] = name
    try:
        result = _run_langfuse_operation_for_request(request, "datasets.list", params)
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse datasets list failed: {exc}")
    return {"ok": True, "data": result}


@router.post("/api/langfuse/datasets")
def create_langfuse_dataset(payload: DatasetCreateRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "langfuse:write")
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "datasets.create",
            {
                "name": payload.name,
                "description": payload.description,
                "metadata": payload.metadata,
            },
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse dataset create failed: {exc}")

    _record_audit(
        request,
        action="langfuse.dataset.create",
        resource_type="langfuse_dataset",
        resource_id=payload.name,
    )
    return {"ok": True, "data": result}


@router.post("/api/langfuse/datasets/{dataset_id}/items:upsert")
def upsert_langfuse_dataset_items(
    dataset_id: str,
    payload: DatasetItemsUpsertRequest,
    request: Request,
) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "langfuse:write")
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "dataset.items.upsert",
            {"dataset_id": dataset_id, "items": payload.items},
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse dataset items upsert failed: {exc}")

    _record_audit(
        request,
        action="langfuse.dataset.items.upsert",
        resource_type="langfuse_dataset",
        resource_id=dataset_id,
        detail={"item_count": len(payload.items)},
    )
    return {"ok": True, "data": result}


@router.post("/api/langfuse/datasets/{dataset_id}/import-from-traces")
def import_langfuse_dataset_from_traces(
    dataset_id: str,
    payload: DatasetImportFromTracesRequest,
    request: Request,
) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "langfuse:write")
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "datasets.import_from_traces",
            {
                "dataset_id": dataset_id,
                "trace_ids": payload.trace_ids,
                "item_template": payload.item_template,
            },
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse dataset import failed: {exc}")

    _record_audit(
        request,
        action="langfuse.dataset.import_from_traces",
        resource_type="langfuse_dataset",
        resource_id=dataset_id,
        detail={"trace_count": len(payload.trace_ids)},
    )
    return {"ok": True, "data": result}


# ---------------------------------------------------------------------------
# Scores / Annotations
# ---------------------------------------------------------------------------

@router.post("/api/langfuse/scores")
def create_langfuse_score(payload: ScoreCreateRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "langfuse:write")
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "score.create",
            {
                "name": payload.name,
                "value": payload.value,
                "trace_id": payload.trace_id,
                "observation_id": payload.observation_id,
                "comment": payload.comment,
                "metadata": payload.metadata,
            },
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse score create failed: {exc}")

    _record_audit(
        request,
        action="langfuse.score.create",
        resource_type="langfuse_score",
        resource_id=payload.name,
    )
    return {"ok": True, "data": result}


@router.post("/api/langfuse/annotations")
def create_langfuse_annotation(payload: AnnotationCreateRequest, request: Request) -> dict[str, Any]:
    _require_role(request, "owner")
    _require_scope(request, "langfuse:write")
    try:
        result = _run_langfuse_operation_for_request(
            request,
            "annotation.create",
            {
                "label": payload.label,
                "trace_id": payload.trace_id,
                "observation_id": payload.observation_id,
                "comment": payload.comment,
                "metadata": payload.metadata,
            },
        )
    except LangfuseControlError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"langfuse annotation create failed: {exc}")

    _record_audit(
        request,
        action="langfuse.annotation.create",
        resource_type="langfuse_annotation",
        resource_id=payload.label,
    )
    return {"ok": True, "data": result}
