"""Measure graph CPU overhead only; run from apps/async-agent with its venv."""

import asyncio
import importlib.metadata
import json
import platform
import statistics
import sys
import time
from datetime import datetime, timezone
from pathlib import Path

sys.path.insert(0, str(Path.cwd()))

from loguru import logger

logger.remove()

from app.services.workflow_graph_builder import WorkflowGraphBuilder


class ProbeBuilder(WorkflowGraphBuilder):
    def __init__(self, config, timing):
        super().__init__(config)
        self.timing = timing

    def _build_node_handler(self, node_def):
        handler = super()._build_node_handler(node_def)

        async def marked(state):
            self.timing.setdefault("first_node_ns", time.perf_counter_ns())
            return await handler(state)

        return marked


def summary(values):
    ordered = sorted(values)
    return {
        "p50_ms": round(statistics.median(ordered), 3),
        "p95_ms": round(ordered[int(0.95 * (len(ordered) - 1))], 3),
        "p99_ms": round(ordered[int(0.99 * (len(ordered) - 1))], 3),
    }


async def main():
    output = {
        "date": datetime.now(timezone.utc).date().isoformat(),
        "python": platform.python_version(),
        "langgraph": importlib.metadata.version("langgraph"),
        "samples": 200,
        "warmups": 20,
        "limitations": [
            "Sequential CPU-only synthetic passthrough graphs",
            "No ingress, auth, broker, DB, checkpoint, tools, model, or telemetry exporter",
            "First node is a synthetic no-op, not specialist/model readiness",
        ],
        "measurements": [],
    }
    for count in [1, 10, 50]:
        nodes = [
            {"id": f"n{i}", "type": "passthrough", "config": {}}
            for i in range(count)
        ]
        config = {
            "name": "isolated-start-probe",
            "nodes": nodes,
            "edges": [
                {"from": "START", "to": "n0"},
                *[
                    {"from": f"n{i}", "to": f"n{i+1}"}
                    for i in range(count - 1)
                ],
                {"from": f"n{count-1}", "to": "END"},
            ],
        }
        builds, starts, full = [], [], []
        for i in range(output["warmups"] + output["samples"]):
            timing = {}
            before = time.perf_counter_ns()
            graph = ProbeBuilder(config, timing).build()
            compiled = time.perf_counter_ns()
            result = await graph.ainvoke(
                {
                    "node_results": {},
                    "context": {},
                    "input_parameters": {},
                    "vars": {},
                },
                config={"recursion_limit": count + 5},
            )
            after = time.perf_counter_ns()
            assert len(result["node_results"]) == count
            if i >= output["warmups"]:
                builds.append((compiled - before) / 1e6)
                starts.append((timing["first_node_ns"] - compiled) / 1e6)
                full.append((after - compiled) / 1e6)
        output["measurements"].append(
            {
                "nodes": count,
                "compile": summary(builds),
                "invoke_to_first_node": summary(starts),
                "whole_graph": summary(full),
            }
        )
    print(json.dumps(output, indent=2))


if __name__ == "__main__":
    asyncio.run(main())
