from datetime import datetime
from typing import Optional

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel

from wiki_engine import db
from wiki_engine import service


router = APIRouter(prefix="/api/wiki", tags=["wiki"])


class WikiSyncFileRequest(BaseModel):
    kb_id: Optional[str] = None
    kb_name: Optional[str] = None
    use_llm: bool = False
    background: bool = True


class WikiSyncKbRequest(BaseModel):
    kb_id: Optional[str] = None
    kb_name: Optional[str] = None
    limit: Optional[int] = None
    use_llm: bool = False
    background: bool = True


class WikiSearchRequest(BaseModel):
    query: str
    kb_id: Optional[str] = None
    kb_name: Optional[str] = None
    limit: int = 8
    include_slices: bool = True


class WikiDeleteKbRequest(BaseModel):
    kb_id: Optional[str] = None
    kb_name: Optional[str] = None


class WikiDeleteFileRequest(BaseModel):
    file_id: str


class WikiDeleteTimeRequest(BaseModel):
    before: Optional[datetime] = None
    after: Optional[datetime] = None
    kb_id: Optional[str] = None
    kb_name: Optional[str] = None


class WikiDiffKbRequest(BaseModel):
    kb_id: Optional[str] = None
    kb_name: Optional[str] = None
    include_children: bool = False
    limit: int = 200
    parsed_only: bool = True


class WikiReconcileKbRequest(BaseModel):
    kb_id: Optional[str] = None
    kb_name: Optional[str] = None
    include_children: bool = False
    limit: int = 200
    sync_missing: bool = True
    rebuild_stale: bool = True
    delete_orphan: bool = False
    use_llm: bool = False
    dry_run: bool = True


@router.post("/init")
async def init_wiki():
    await db.init_wiki_tables()
    return {"success": True}


@router.post("/sync/file/{file_id}")
async def sync_file(file_id: str, request: WikiSyncFileRequest):
    kb_id = await _resolve_optional_kb_id(request.kb_id, request.kb_name)
    if request.background:
        job_id = await service.start_sync_job(
            target_type="file",
            target_id=file_id,
            kb_id=kb_id,
            use_llm=request.use_llm,
        )
        return {"success": True, "job_id": job_id, "status": "running", "job": await db.get_job(job_id)}
    return await service.sync_file(file_id=file_id, kb_id=kb_id, use_llm=request.use_llm)


@router.post("/sync/kb")
async def sync_kb(request: WikiSyncKbRequest):
    kb_id = await _resolve_required_kb_id(request.kb_id, request.kb_name)
    if request.background:
        job_id = await service.start_sync_job(
            target_type="kb",
            target_id=kb_id,
            kb_id=kb_id,
            limit=request.limit,
            use_llm=request.use_llm,
        )
        return {"success": True, "job_id": job_id, "status": "running", "job": await db.get_job(job_id)}
    return await service.sync_kb(kb_id=kb_id, limit=request.limit, use_llm=request.use_llm)


@router.get("/sync/jobs/{job_id}")
async def get_sync_job(job_id: str):
    job = await db.get_job(job_id)
    if not job:
        raise HTTPException(status_code=404, detail="sync job not found")
    return {"success": True, "job": job}


@router.post("/search")
async def search_wiki(request: WikiSearchRequest):
    if not request.query.strip():
        raise HTTPException(status_code=400, detail="query is empty")
    kb_id = await _resolve_optional_kb_id(request.kb_id, request.kb_name)
    return await service.search_wiki(
        query=request.query,
        kb_id=kb_id,
        limit=request.limit,
        include_slices=request.include_slices,
    )


@router.post("/evidence/search")
async def search_evidence(request: WikiSearchRequest):
    if not request.query.strip():
        raise HTTPException(status_code=400, detail="query is empty")
    kb_id = await _resolve_optional_kb_id(request.kb_id, request.kb_name)
    return await service.evidence_search(
        query=request.query,
        kb_id=kb_id,
        limit=request.limit,
    )


@router.get("/pages/{page_id}")
async def get_page(page_id: int):
    page = await service.get_page(page_id)
    if not page:
        raise HTTPException(status_code=404, detail="wiki page not found")
    return {"success": True, "page": page}


@router.get("/stats")
async def get_stats():
    return {"success": True, "stats": await service.get_stats()}


@router.get("/queue/stats")
async def get_queue_stats():
    return {"success": True, "stats": await service.get_queue_stats()}


@router.get("/tree")
async def get_kb_tree():
    return await service.get_kb_tree()


@router.post("/diff/kb")
async def diff_kb(request: WikiDiffKbRequest):
    kb_id = await _resolve_required_kb_id(request.kb_id, request.kb_name)
    return await service.diff_kb(
        kb_id=kb_id,
        include_children=request.include_children,
        limit=request.limit,
        parsed_only=request.parsed_only,
    )


@router.post("/reconcile/kb")
async def reconcile_kb(request: WikiReconcileKbRequest):
    kb_id = await _resolve_required_kb_id(request.kb_id, request.kb_name)
    return await service.reconcile_kb(
        kb_id=kb_id,
        include_children=request.include_children,
        limit=request.limit,
        sync_missing=request.sync_missing,
        rebuild_stale=request.rebuild_stale,
        delete_orphan=request.delete_orphan,
        use_llm=request.use_llm,
        dry_run=request.dry_run,
    )


@router.post("/delete/by-kb")
async def delete_by_kb(request: WikiDeleteKbRequest):
    kb_id = await _resolve_required_kb_id(request.kb_id, request.kb_name)
    return await service.delete_by_kb(kb_id)


@router.post("/delete/by-file")
async def delete_by_file(request: WikiDeleteFileRequest):
    if not request.file_id.strip():
        raise HTTPException(status_code=400, detail="file_id is empty")
    return await service.delete_by_file(request.file_id)


@router.post("/delete/by-time")
async def delete_by_time(request: WikiDeleteTimeRequest):
    if request.before is None and request.after is None:
        raise HTTPException(status_code=400, detail="before or after is required")
    kb_id = await _resolve_optional_kb_id(request.kb_id, request.kb_name)
    return await service.delete_by_time(
        before=request.before,
        after=request.after,
        kb_id=kb_id,
    )


async def _resolve_required_kb_id(kb_id: Optional[str], kb_name: Optional[str]) -> str:
    resolved = await _resolve_optional_kb_id(kb_id, kb_name)
    if not resolved:
        raise HTTPException(status_code=400, detail="kb_name or kb_id is required")
    return resolved


async def _resolve_optional_kb_id(kb_id: Optional[str], kb_name: Optional[str]) -> Optional[str]:
    if kb_id and kb_id.strip():
        return kb_id.strip()
    if not kb_name or not kb_name.strip():
        return None
    resolved = await service.resolve_kb_reference(kb_name.strip())
    if not resolved.get("success"):
        raise HTTPException(status_code=404, detail=resolved)
    return str(resolved["kb_id"])
