"""
API Server — Phase 9 of Kodara pipeline.

FastAPI server exposing Kodara's memory layer as an HTTP API.
Enables integration with IDE plugins, AI coding assistants, and
custom tooling.

Endpoints:
  POST /index   — Index a repository
  POST /query   — Query the architectural memory
  GET  /graph   — Export dependency graph
  GET  /status  — Index status for a project
  GET  /health  — Health check

Design: project-local storage — every request specifies a project path.
"""

from __future__ import annotations

import time
from pathlib import Path
from typing import Optional

try:
    from fastapi import FastAPI, HTTPException
    from fastapi.middleware.cors import CORSMiddleware
    from pydantic import BaseModel
    import uvicorn
except ImportError:
    raise ImportError(
        "API server requires: pip install fastapi uvicorn"
    )

from ..memory.store import MemoryStore, KodaraIndex, INDEX_VERSION


app = FastAPI(
    title="Kodara API",
    description="Memory Layer for AI Development — Architecture-aware context engine",
    version="0.2.0",
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)


# ── Request / Response models ──────────────────────────────────────────────────

class IndexRequest(BaseModel):
    path: str                       # absolute path to project root
    incremental: bool = True
    generate_summaries: bool = True


class QueryRequest(BaseModel):
    path: str                       # absolute path to project root
    query: str
    max_modules: int = 8
    depth: int = 1


class GraphRequest(BaseModel):
    path: str                       # absolute path to project root
    fmt: str = "json"               # json | dot | mermaid


class IndexResponse(BaseModel):
    success: bool
    repo_id: str
    display_name: str
    file_count: int
    languages: dict
    edge_count: int
    elapsed_seconds: float


# ── Helpers ────────────────────────────────────────────────────────────────────

def _resolve(path: str) -> Path:
    """Resolve and validate project root path."""
    root = Path(path).resolve()
    if not root.exists():
        raise HTTPException(status_code=404, detail=f"Path not found: {root}")
    if not root.is_dir():
        raise HTTPException(status_code=400, detail=f"Path is not a directory: {root}")
    return root


# ── Endpoints ──────────────────────────────────────────────────────────────────

@app.get("/health")
def health():
    return {"status": "ok", "version": "0.2.0"}


@app.get("/status")
def status(path: str):
    """Check if a project is indexed."""
    root = _resolve(path)
    store = MemoryStore(root)
    idx = store.load()
    if not idx:
        return {"indexed": False, "path": str(root)}
    return {
        "indexed": True,
        "path": str(root),
        "repo_id": idx.repo_id,
        "display_name": idx.display_name,
        "file_count": idx.file_count,
        "languages": idx.language_counts,
        "indexed_at": idx.indexed_at,
    }


@app.post("/index")
def index_repo(req: IndexRequest):
    """Index a repository and store memory locally in .kodara/."""
    from ..indexer.scanner import RepositoryScanner, FreePlanLimitError
    from ..graph.dependency import DependencyGraph
    from ..summarizer import generate_summaries

    root = _resolve(req.path)
    store = MemoryStore(root)
    t0 = time.time()

    existing = store.load() if req.incremental else None

    # Scan
    try:
        scanner = RepositoryScanner(root)
        scan = scanner.scan()
    except FreePlanLimitError as e:
        raise HTTPException(
            status_code=402,
            detail=f"Free plan limit: {e.file_count}+ files. Upgrade to Pro at getkodara.dev/pro"
        )

    # Graph
    graph = DependencyGraph()
    graph.build(scan)
    graph_data = graph.to_dict()

    # Modules
    modules: dict[str, dict] = {}
    for path, info in scan.files.items():
        existing_summary = ""
        if existing and path in existing.modules:
            ex_mod = existing.modules[path]
            if ex_mod.get("file_hash") == info.hash:
                existing_summary = ex_mod.get("summary", "")

        modules[path] = {
            "path": path,
            "module_name": graph.node_data(path).get("module_name", Path(path).stem),
            "language": info.language,
            "classes": info.classes,
            "functions": info.functions,
            "exports": info.exports,
            "imports": info.imports,
            "file_hash": info.hash,
            "summary": existing_summary,
            "dependencies": graph.get_dependencies(path),
            "dependents": graph.get_dependents(path),
        }

    # Summaries
    if req.generate_summaries:
        try:
            summaries = generate_summaries(modules)
            for path, summary in summaries.items():
                if path in modules:
                    modules[path]["summary"] = summary
        except Exception:
            pass

    index = KodaraIndex(
        repo_id=MemoryStore.repo_id_from_path(root),
        display_name=root.name,
        source_root=str(root),
        indexed_at=time.time(),
        index_version=INDEX_VERSION,
        file_count=len(scan.files),
        language_counts=scan.languages,
        modules=modules,
        graph_edges=graph_data["edges"],
        file_hashes={p: f.hash for p, f in scan.files.items()},
    )
    store.save(index)

    return IndexResponse(
        success=True,
        repo_id=index.repo_id,
        display_name=root.name,
        file_count=len(scan.files),
        languages=scan.languages,
        edge_count=len(graph_data["edges"]),
        elapsed_seconds=round(time.time() - t0, 2),
    )


@app.post("/query")
def query_context(req: QueryRequest):
    """Query the architectural memory of a project."""
    from ..context_engine.engine import ContextEngine

    root = _resolve(req.path)
    store = MemoryStore(root)

    if not store.exists():
        raise HTTPException(
            status_code=404,
            detail=f"Project not indexed. Run: kodara init {req.path}"
        )

    engine = ContextEngine(store)
    try:
        result = engine.query(req.query, req.max_modules, req.depth)
        formatted = engine.format_context(result)
        return {
            "query": result.query,
            "modules": result.modules,
            "summaries": result.summaries,
            "relevant_files": result.relevant_files,
            "graph_fragment": result.graph_fragment,
            "formatted_context": formatted,
            "token_estimate": result.token_estimate,
            "total_modules_searched": result.total_modules_searched,
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/graph")
def get_graph(path: str, fmt: str = "json"):
    """Export dependency graph for a project."""
    from ..architecture.visualizer import ArchitectureVisualizer

    root = _resolve(path)
    store = MemoryStore(root)
    index = store.load()

    if not index:
        raise HTTPException(
            status_code=404,
            detail=f"Project not indexed. Run: kodara init {path}"
        )

    viz = ArchitectureVisualizer()
    if fmt == "dot":
        return {"format": "dot", "content": viz.to_graphviz(index)}
    if fmt == "mermaid":
        return {"format": "mermaid", "content": viz.to_mermaid(index)}
    return viz.to_json(index)


# ── Entry point ────────────────────────────────────────────────────────────────

def start_server(host: str = "127.0.0.1", port: int = 8742):
    uvicorn.run(app, host=host, port=port, log_level="info")
