"""HTTP routes for docsctx."""

from __future__ import annotations

import asyncio
import json
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import AsyncGenerator

from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field

from common.version import __version__
from docsctx.config import DocsSettings
from docsctx.db.repository import DocsRepository
from docsctx.finalize import run_finalize
from docsctx.indexer import DocsIndexer


class SearchRequest(BaseModel):
    query: str
    mode: str = "local"
    top_k: int = Field(default=5, ge=1, le=50)
    doc_type: str = ""


class UploadDocument(BaseModel):
    filename: str
    content: str
    doc_type: str = "text"


class UploadRequest(BaseModel):
    documents: list[UploadDocument]


def build_router(repository: DocsRepository, settings: DocsSettings | None = None) -> APIRouter:
    """Create API routes bound to a repository instance."""
    router = APIRouter(prefix="/api")
    resolved_settings = settings or DocsSettings()

    @router.get("/stats")
    async def stats() -> dict[str, int]:
        return repository.get_stats().to_dict()

    @router.get("/version")
    async def version() -> dict[str, str]:
        return {"version": __version__}

    @router.get("/documents")
    async def documents(doc_type: str = "", limit: int = 20, offset: int = 0) -> list[dict[str, object]]:
        return repository.list_documents(doc_type=doc_type, limit=limit, offset=offset)

    @router.get("/documents/{document_id}")
    async def document(document_id: int) -> dict[str, object]:
        payload = repository.get_document(document_id)
        if payload is None:
            raise HTTPException(status_code=404, detail="document not found")
        return payload

    @router.get("/documents/{document_id}/versions")
    async def document_versions(document_id: int) -> list[dict[str, object]]:
        payload = repository.get_document(document_id)
        if payload is None:
            raise HTTPException(status_code=404, detail="document not found")
        return repository.list_document_versions(document_id)

    @router.post("/search")
    async def search(request: SearchRequest) -> dict[str, object]:
        matches = repository.search(request.query, top_k=request.top_k, doc_type=request.doc_type)
        return {
            "mode": request.mode,
            "query": request.query,
            "results": [match.to_dict() for match in matches],
        }

    @router.post("/upload")
    async def upload(request: UploadRequest) -> dict[str, int]:
        indexer = DocsIndexer(
            repository,
            chunk_size=resolved_settings.chunk_size,
            chunk_overlap=resolved_settings.chunk_overlap,
        )
        indexed_documents = 0
        skipped_documents = 0
        with TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)
            for document in request.documents:
                suffix = {
                    "markdown": ".md",
                    "html": ".html",
                    "text": ".txt",
                }.get(document.doc_type, ".txt")
                target = temp_path / f"{Path(document.filename).stem}{suffix}"
                target.write_text(document.content, encoding="utf-8")
                summary = indexer.index_path(target)
                indexed_documents += summary.indexed_documents
                skipped_documents += summary.skipped_documents
        return {"indexed_documents": indexed_documents, "skipped_documents": skipped_documents}

    @router.post("/finalize")
    async def finalize() -> dict[str, int]:
        return run_finalize(repository, resolved_settings)

    @router.get("/upload/progress")
    async def upload_progress(task_id: str = "") -> StreamingResponse:
        """Server-Sent Events endpoint for upload progress tracking."""

        async def _event_generator() -> AsyncGenerator[str, None]:
            # Emit an initial connected event
            yield f"data: {json.dumps({'event': 'connected', 'task_id': task_id})}\n\n"
            # Poll for up to 60s in 1s intervals; real progress would come from a shared task store
            for i in range(60):
                await asyncio.sleep(1.0)
                stats = repository.get_stats().to_dict()
                payload = json.dumps({"event": "stats", "tick": i + 1, **stats})
                yield f"data: {payload}\n\n"
            yield f"data: {json.dumps({'event': 'done'})}\n\n"

        return StreamingResponse(
            _event_generator(),
            media_type="text/event-stream",
            headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
        )

    @router.get("/communities")
    async def communities() -> list[dict[str, object]]:
        return [community.to_dict() for community in repository.list_communities()]

    @router.get("/communities/{community_id}")
    async def community(community_id: int) -> dict[str, object]:
        payload = repository.get_community(community_id)
        if payload is None:
            raise HTTPException(status_code=404, detail="community not found")
        return payload.to_dict()

    @router.get("/entities")
    async def entities(
        document_id: int = 0,
        name: str = "",
        limit: int = 50,
        offset: int = 0,
    ) -> list[dict[str, object]]:
        """List entities, optionally filtered by document_id or name substring."""
        if document_id:
            rows = repository.list_entities(document_id)
        else:
            rows = repository.list_all_entities(limit=limit, offset=offset)
        if name:
            name_lower = name.lower()
            rows = [r for r in rows if name_lower in (r.get("name") or "").lower()]
        return rows[:limit]

    @router.get("/entities/{entity_id}")
    async def entity(entity_id: int) -> dict[str, object]:
        payload = repository.get_entity(entity_id)
        if payload is None:
            raise HTTPException(status_code=404, detail="entity not found")
        return payload

    @router.get("/graph/neighborhood")
    async def graph_neighborhood(
        entity_name: str,
        depth: int = 2,
        max_nodes: int = 50,
    ) -> dict[str, object]:
        """Return the graph neighborhood (BFS) around an entity."""
        result = repository.get_graph_neighborhood(
            entity_name, depth=max(1, min(depth, 5)), max_nodes=max(1, min(max_nodes, 200))
        )
        if result is None:
            raise HTTPException(status_code=404, detail=f"entity '{entity_name}' not found")
        return result

    return router
