"""REST API routes — serves data to the NextJS frontend."""

from __future__ import annotations

from typing import TYPE_CHECKING

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware

from ..core.graph.store import GraphStore
from ..core.search.bm25_index import SymbolSearchIndex

if TYPE_CHECKING:
    from ..core.search.hybrid_search import HybridSearch

app = FastAPI(title="CodeGraph API", version="0.1.0")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# Shared state (initialized in cli.py)
_graph: GraphStore | None = None
_search: SymbolSearchIndex | None = None
_hybrid: HybridSearch | None = None


def init_api(
    graph: GraphStore,
    search: SymbolSearchIndex,
    hybrid: HybridSearch | None = None,
):
    global _graph, _search, _hybrid
    _graph = graph
    _search = search
    _hybrid = hybrid


@app.get("/api/stats")
def get_stats():
    if not _graph:
        raise HTTPException(503, "Graph not initialized")
    return _graph.get_stats()


@app.get("/api/symbols")
def list_symbols(q: str = "", limit: int = 50, mode: str = "auto"):
    """List/search symbols for the graph viewer.

    Args:
        q: Search query
        limit: Max results (capped at 200)
        mode: "auto" (hybrid if available), "keyword", "semantic"
    """
    if not _graph:
        raise HTTPException(503, "Graph not initialized")

    limit = min(max(limit, 1), 200)  # Cap between 1 and 200

    if q:
        search_mode = mode
        if mode == "auto" and _hybrid:
            results = _hybrid.search(q, limit=limit)
            search_mode = "hybrid"
        elif mode == "semantic" and _hybrid:
            results = _hybrid._vector_search(q, limit=limit)
            search_mode = "semantic"
        elif _search:
            results = _search.search(q, limit=limit)
            search_mode = "keyword"
        else:
            results = []
            search_mode = "none"
        return {"results": results, "mode": search_mode}

    symbols = _graph.get_all_symbols()[:limit]
    return {"results": symbols}


@app.get("/api/symbol/{uid:path}")
def get_symbol(uid: str):
    if not _graph:
        raise HTTPException(503, "Graph not initialized")

    sym = _graph.get_symbol(uid)
    if not sym:
        raise HTTPException(404, f"Symbol not found: {uid}")

    ctx = _graph.get_context(uid)
    return {"symbol": sym, **ctx}


@app.get("/api/graph")
def get_graph_data(limit: int = 200):
    """Get nodes and edges for D3.js visualization."""
    limit = min(max(limit, 1), 500)  # Cap graph nodes

    if not _graph:
        raise HTTPException(503, "Graph not initialized")

    with _graph.driver.session() as session:
        # Nodes (symbols only, not files/folders for cleaner viz)
        node_result = session.run(
            "MATCH (n:Symbol) "
            "WHERE n.node_type IN ['Function', 'Class', 'Method'] "
            "RETURN n.uid AS id, n.name AS name, n.node_type AS type, "
            "n.file_path AS file LIMIT $limit",
            limit=limit,
        )
        nodes = [dict(r) for r in node_result]

        # Edges between those nodes
        node_ids = {n["id"] for n in nodes}
        edge_result = session.run(
            "MATCH (a:Symbol)-[r]->(b:Symbol) "
            "WHERE a.uid IN $ids AND b.uid IN $ids "
            "RETURN a.uid AS source, b.uid AS target, type(r) AS type",
            ids=list(node_ids),
        )
        edges = [dict(r) for r in edge_result]

    return {"nodes": nodes, "edges": edges}


@app.get("/api/clusters")
def get_clusters():
    """Get all functional clusters."""
    if not _graph:
        raise HTTPException(503, "Graph not initialized")
    return {"clusters": _graph.get_clusters()}


@app.get("/api/processes")
def get_processes():
    """Get all execution flows."""
    if not _graph:
        raise HTTPException(503, "Graph not initialized")
    return {"processes": _graph.get_processes()}


@app.get("/api/process/{process_id:path}")
def get_process(process_id: str):
    """Get a specific execution flow with steps."""
    if not _graph:
        raise HTTPException(503, "Graph not initialized")
    detail = _graph.get_process_detail(process_id)
    if not detail:
        raise HTTPException(404, f"Process not found: {process_id}")
    return detail
