"""Watcher observability — status, quarantine, manual control."""

from __future__ import annotations

import logging

from fastapi import APIRouter, HTTPException, Request
from pydantic import BaseModel

log = logging.getLogger(__name__)

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


@router.get("/status")
async def watcher_status(request: Request) -> dict:
    """Return current watcher state: cycles, known files, quarantine, errors."""
    watcher = getattr(request.app.state, "raw_watcher", None)
    if not watcher:
        raise HTTPException(status_code=503, detail="Watcher not initialized")
    try:
        return watcher.get_status()
    except Exception as exc:
        log.exception("watcher status failed")
        raise HTTPException(status_code=500, detail=str(exc)) from exc


@router.get("/quarantine")
async def list_quarantine(request: Request) -> dict:
    """List all quarantined files across all KBs."""
    vault = getattr(request.app.state, "vault", None)
    if not vault or not vault.paths:
        raise HTTPException(status_code=503, detail="Vault not available")

    kb_dir = vault.paths.resolve("knowledge")
    items: list[dict] = []
    if kb_dir.is_dir():
        for kb in kb_dir.iterdir():
            if not kb.is_dir():
                continue
            q = kb / "inbox" / ".quarantine"
            if not q.is_dir():
                continue
            for f in q.iterdir():
                if not f.is_file():
                    continue
                try:
                    st = f.stat()
                    items.append({
                        "kb": kb.name,
                        "filename": f.name,
                        "size": st.st_size,
                        "quarantined_at": st.st_mtime,
                    })
                except OSError:
                    continue

    return {"count": len(items), "items": items}


class QuarantineAction(BaseModel):
    kb: str
    filename: str


@router.post("/quarantine/restore")
async def restore_quarantined(request: Request, body: QuarantineAction) -> dict:
    """Move a quarantined file back to the KB inbox for retry."""
    vault = getattr(request.app.state, "vault", None)
    if not vault or not vault.paths:
        raise HTTPException(status_code=503, detail="Vault not available")

    q = vault.paths.resolve(f"knowledge/{body.kb}/inbox/.quarantine") / body.filename
    inbox = vault.paths.resolve(f"knowledge/{body.kb}/inbox") / body.filename
    if not q.is_file():
        raise HTTPException(status_code=404, detail=f"Not quarantined: {body.filename}")
    if inbox.exists():
        raise HTTPException(status_code=409, detail=f"File already in inbox: {body.filename}")
    try:
        q.rename(inbox)
    except OSError as exc:
        raise HTTPException(status_code=500, detail=f"Restore failed: {exc}") from exc
    return {"status": "restored", "kb": body.kb, "filename": body.filename}


@router.post("/quarantine/delete")
async def delete_quarantined(request: Request, body: QuarantineAction) -> dict:
    """Permanently delete a quarantined file."""
    vault = getattr(request.app.state, "vault", None)
    if not vault or not vault.paths:
        raise HTTPException(status_code=503, detail="Vault not available")

    q = vault.paths.resolve(f"knowledge/{body.kb}/inbox/.quarantine") / body.filename
    if not q.is_file():
        raise HTTPException(status_code=404, detail=f"Not quarantined: {body.filename}")
    try:
        q.unlink()
    except OSError as exc:
        raise HTTPException(status_code=500, detail=f"Delete failed: {exc}") from exc
    return {"status": "deleted", "kb": body.kb, "filename": body.filename}


@router.post("/quarantine/restore-all")
async def restore_all_quarantined(request: Request) -> dict:
    """Move every quarantined file back to its KB inbox for retry.

    Useful after recovering from an LLM outage: previously-timed-out files
    go back in the queue and the watcher picks them up on its next cycle.
    """
    vault = getattr(request.app.state, "vault", None)
    if not vault or not vault.paths:
        raise HTTPException(status_code=503, detail="Vault not available")

    kb_dir = vault.paths.resolve("knowledge")
    restored: list[dict] = []
    if kb_dir.is_dir():
        for kb in kb_dir.iterdir():
            if not kb.is_dir():
                continue
            q = kb / "inbox" / ".quarantine"
            if not q.is_dir():
                continue
            for f in list(q.iterdir()):
                if not f.is_file():
                    continue
                dest = kb / "inbox" / f.name
                if dest.exists():
                    continue  # don't overwrite active inbox items
                try:
                    f.rename(dest)
                    restored.append({"kb": kb.name, "filename": f.name})
                except OSError as exc:
                    log.warning("restore %s failed: %s", f, exc)
    return {"status": "ok", "count": len(restored), "restored": restored}


@router.post("/scan")
async def trigger_scan(request: Request) -> dict:
    """Trigger an immediate watcher scan (useful after adding files)."""
    watcher = getattr(request.app.state, "raw_watcher", None)
    if not watcher:
        raise HTTPException(status_code=503, detail="Watcher not initialized")
    try:
        ingested = await watcher.check_and_ingest()
        return {
            "status": "ok",
            "ingested": len(ingested) if ingested else 0,
        }
    except Exception as exc:
        log.exception("watcher scan failed")
        raise HTTPException(status_code=500, detail=str(exc)) from exc
