"""Pathway endpoint handlers -- list, get, run, export, graph.

Read-only discovery + execution. Pathways are authored via YAML/CLI,
not created or mutated through the HTTP API.
"""

from __future__ import annotations

import logging
import time

from aiohttp import web

from core.observability import (
    pop_trace_depth,
    push_trace_depth,
    reset_log_execution_id,
    reset_log_pathway_id,
    set_log_execution_id,
    set_log_pathway_id,
)
from core.pathways.store import serialize_node
from server.errors import ErrorCode, sanitize_error_message
from server.transport.schemas import (
    PathwayGraphResponse,
    PathwayListResponse,
    PathwayResponse,
    PathwayRunRequest,
    PathwayRunResponse,
    validate_request,
)
from server.transport.utils import (
    error_response,
    get_request_id,
    get_runtime,
    json_response,
)

logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# List / Get
# ---------------------------------------------------------------------------


async def handle_list_pathways(request: web.Request) -> web.Response:
    """GET /api/v1/pathways - List all pathways."""
    request_id = get_request_id(request)
    source_filter = request.query.get("source")

    runtime = get_runtime(request)
    service = runtime.pathway_service
    pathways = service.list(source_filter=source_filter)

    logger.info(
        "List pathways  source=%s  count=%d  [request_id=%s]",
        source_filter or "all",
        len(pathways),
        request_id,
    )

    resp = PathwayListResponse(
        pathways=[p.to_dict() for p in pathways],
        count=len(pathways),
    )
    return json_response(request, resp.model_dump(mode="json"))


async def handle_get_pathway(request: web.Request) -> web.Response:
    """GET /api/v1/pathways/{pathway_id} - Get pathway details."""
    pathway_id = str(request.match_info.get("pathway_id") or "").strip()
    if not pathway_id:
        return error_response(
            request, "pathway_id is required", ErrorCode.VALIDATION_ERROR, status=400
        )

    runtime = get_runtime(request)
    service = runtime.pathway_service
    pathway = service.load(pathway_id)

    if pathway is None:
        return error_response(
            request,
            f"Pathway not found: {pathway_id}",
            ErrorCode.PATHWAY_NOT_FOUND,
            status=404,
        )

    meta = service.get_meta(pathway_id)

    nodes = [serialize_node(node) for node in pathway.nodes.values()]
    connections = [{"from": c.from_node, "to": c.to_node} for c in pathway.connections]

    resp = PathwayResponse(
        pathway={
            "id": pathway.id,
            "name": pathway.name,
            "description": pathway.description,
            "nodes": nodes,
            "connections": connections,
            "metadata": dict(pathway.metadata),
        },
        meta=meta.to_dict() if meta else None,
    )
    return json_response(request, resp.model_dump(mode="json"))


# ---------------------------------------------------------------------------
# Export / Graph
# ---------------------------------------------------------------------------


async def handle_export_pathway(request: web.Request) -> web.Response:
    """GET /api/v1/pathways/{pathway_id}/export - Export pathway as JSON."""
    pathway_id = str(request.match_info.get("pathway_id") or "").strip()
    if not pathway_id:
        return error_response(
            request, "pathway_id is required", ErrorCode.VALIDATION_ERROR, status=400
        )

    runtime = get_runtime(request)
    service = runtime.pathway_service
    export_data = service.export(pathway_id)

    if export_data is None:
        return error_response(
            request,
            f"Pathway not found: {pathway_id}",
            ErrorCode.PATHWAY_NOT_FOUND,
            status=404,
        )

    return json_response(request, export_data)


async def handle_pathway_graph(request: web.Request) -> web.Response:
    """GET /api/v1/pathways/{pathway_id}/graph - Return compact graph payload."""
    pathway_id = str(request.match_info.get("pathway_id") or "").strip()
    if not pathway_id:
        return error_response(
            request, "pathway_id is required", ErrorCode.VALIDATION_ERROR, status=400
        )

    runtime = get_runtime(request)
    service = runtime.pathway_service
    pathway = service.load(pathway_id)

    if pathway is None:
        return error_response(
            request,
            f"Pathway not found: {pathway_id}",
            ErrorCode.PATHWAY_NOT_FOUND,
            status=404,
        )

    meta = service.get_meta(pathway_id)

    nodes = []
    for nid, node in (pathway.nodes or {}).items():
        ntype = getattr(node, "type", node.__class__.__name__)
        prompt = getattr(node, "prompt", None)
        if isinstance(prompt, str) and len(prompt) > 160:
            prompt = prompt[:157] + "..."
        nodes.append({"id": str(nid), "type": str(ntype), "prompt": prompt})

    conns = [[c.from_node, c.to_node] for c in (pathway.connections or [])]
    resp = PathwayGraphResponse(
        pathway_id=pathway.id,
        source=meta.source if meta else None,
        node_count=len(nodes),
        nodes=nodes,
        connections=conns,
    )
    return json_response(request, resp.model_dump(mode="json"))


# ---------------------------------------------------------------------------
# Execution
# ---------------------------------------------------------------------------


async def handle_run_pathway(request: web.Request) -> web.Response:
    """POST /api/v1/pathways/{pathway_id}/run - Run any pathway."""
    request_id = get_request_id(request)

    pathway_id = str(request.match_info.get("pathway_id") or "").strip()
    if not pathway_id:
        return error_response(
            request, "pathway_id is required", ErrorCode.VALIDATION_ERROR, status=400
        )

    try:
        body = await request.json()
    except Exception:
        body = {}

    validated, error_resp = validate_request(PathwayRunRequest, body)
    if error_resp:
        logger.info(
            "Validation failed for run_pathway %s  [request_id=%s]: %s",
            pathway_id,
            request_id,
            error_resp.error,
        )
        return json_response(request, error_resp.model_dump(mode="json"), status=400)

    assert validated is not None
    inputs = validated.inputs

    start = time.time()

    runtime = get_runtime(request)
    service = runtime.pathway_service
    pathway = service.load(pathway_id)

    if pathway is None:
        return error_response(
            request,
            f"Pathway not found: {pathway_id}",
            ErrorCode.PATHWAY_NOT_FOUND,
            status=404,
        )

    tok_p = set_log_pathway_id(pathway_id)
    tok_d = push_trace_depth()
    tok_x = None

    meta = service.get_meta(pathway_id)
    logger.info(
        "Pathway executing: %s  name=%s  source=%s  nodes=%d  [request_id=%s]",
        pathway_id,
        pathway.name,
        (meta.source if meta else "unknown"),
        len(pathway.nodes),
        request_id,
    )

    vm = getattr(runtime, "pathway_vm", None)

    if vm is None:
        return error_response(
            request,
            "No pathway executor available",
            ErrorCode.SERVICE_UNAVAILABLE,
            status=503,
        )

    try:
        record = await vm.execute(pathway, inputs)
        tok_x = set_log_execution_id(getattr(record, "id", None))
        duration_ms = (time.time() - start) * 1000

        logger.info(
            "Pathway completed: %s  execution=%s  success=%s  duration=%.1fms  [request_id=%s]",
            pathway_id,
            record.id[:8] if record.id else "unknown",
            record.success,
            duration_ms,
            request_id,
        )

        resp = PathwayRunResponse(
            outputs=record.outputs,
            execution_id=record.id,
            status=record.status.value,
            duration_ms=duration_ms,
            error=record.error,
        )
        return json_response(request, resp.model_dump(mode="json"))

    except Exception as e:
        duration_ms = (time.time() - start) * 1000
        logger.error(
            "Pathway execution failed: %s  duration=%.1fms  [request_id=%s]: %s",
            pathway_id,
            duration_ms,
            request_id,
            str(e),
            exc_info=True,
        )
        error_msg = sanitize_error_message(str(e), is_production=None)
        return error_response(
            request,
            error_msg,
            ErrorCode.PATHWAY_EXECUTION_FAILED,
            status=500,
            details={"duration_ms": duration_ms},
        )
    finally:
        if tok_x is not None:
            reset_log_execution_id(tok_x)
        pop_trace_depth(tok_d)
        reset_log_pathway_id(tok_p)


__all__ = [
    "handle_export_pathway",
    "handle_get_pathway",
    "handle_list_pathways",
    "handle_pathway_graph",
    "handle_run_pathway",
]
