import threading

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

from backend.config import MEMORY_DIR, MEMORY_INDEX_FILE
from backend.learning_feedback import get_playbook_stats, get_policy_feedback
from backend.learning_pipeline import (
    finalize_pending_learning_candidates,
    get_learning_candidates,
    review_learning_candidate,
)
from backend.memory import (
    _SYSTEM_LOG_FILE,
    _list_sessions,
    _load_memory_index,
    _load_session,
    _prune_with_usage,
    _reindex_all_sessions,
    _save_memory_index,
    _save_session,
    _session_path,
    invalidate_agent_system_cache,
    log_system_event,
    read_system_log,
)
from backend.wiki_memory import (
    delete_wiki_draft,
    get_wiki_draft,
    get_wiki_status,
    list_wiki_drafts,
    publish_wiki_draft,
    review_wiki_draft,
)


router = APIRouter()
_learning_finalize_lock = threading.Lock()
_learning_finalize_in_flight = False


def _run_learning_candidate_finalization(max_items: int) -> None:
    global _learning_finalize_in_flight
    try:
        finalized = finalize_pending_learning_candidates(max_items=max_items)
        if finalized.get("changed"):
            invalidate_agent_system_cache()
            log_system_event(
                "learning",
                "Auto-reviewed pending learnings",
                {
                    "reviewed": str(int(finalized.get("reviewed") or 0)),
                    "promoted": str(int(finalized.get("promoted") or 0)),
                },
            )
    finally:
        with _learning_finalize_lock:
            _learning_finalize_in_flight = False


def _schedule_learning_candidate_finalization(max_items: int) -> bool:
    global _learning_finalize_in_flight
    safe_limit = max(1, int(max_items or 1))
    with _learning_finalize_lock:
        if _learning_finalize_in_flight:
            return False
        _learning_finalize_in_flight = True
    threading.Thread(
        target=_run_learning_candidate_finalization,
        args=(safe_limit,),
        daemon=True,
    ).start()
    return True


class _LearningCandidateReviewRequest(BaseModel):
    candidate_id: str
    approved: bool
    reason: str = ""
    reviewer: str = "manual_api"


class _WikiDraftReviewRequest(BaseModel):
    approved: bool
    reason: str = ""
    reviewer: str = "manual_api"


class _WikiDraftPublishRequest(BaseModel):
    reviewer: str = "manual_api"
    title: str = ""
    target_page: str = ""


@router.get("/api/sessions")
def api_list_sessions():
    return {"sessions": _list_sessions()}


@router.get("/api/sessions/{session_id}")
def api_get_session(session_id: str):
    session = _load_session(session_id)
    if session is None:
        raise HTTPException(status_code=404, detail="Session not found")
    return session


@router.delete("/api/sessions/{session_id}")
def api_delete_session(session_id: str):
    try:
        path = _session_path(session_id)
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid session id")
    if path.exists():
        path.unlink()
    return {"ok": True}


@router.delete("/api/sessions")
def api_clear_sessions():
    for session_file in MEMORY_DIR.glob("*.json"):
        session_file.unlink()
    if MEMORY_INDEX_FILE.exists():
        MEMORY_INDEX_FILE.unlink()
    return {"ok": True}


@router.patch("/api/sessions/{session_id}/no-learn")
async def api_session_no_learn(session_id: str, request: Request):
    session = _load_session(session_id)
    if session is None:
        raise HTTPException(status_code=404, detail="Session not found")
    try:
        body = await request.json()
    except Exception:
        body = {}
    no_learn = bool(body.get("no_learn", True))
    session["no_learn"] = no_learn
    _save_session(session)
    if no_learn:
        index = _load_memory_index()
        if session_id in index:
            del index[session_id]
            _save_memory_index(index)
    return {"ok": True, "no_learn": no_learn}


@router.post("/api/memory/reindex")
def api_memory_reindex():
    threading.Thread(target=_reindex_all_sessions, daemon=True).start()
    return {"ok": True, "message": "Reindexing started in background."}


@router.post("/api/memory/prune")
def api_memory_prune(max_age_days: int = 30):
    result = _prune_with_usage(max_age_days=max_age_days)
    return {"ok": True, **result}


@router.get("/api/system-log")
def api_system_log():
    return {"events": read_system_log()}


@router.delete("/api/system-log")
def api_system_log_clear():
    try:
        _SYSTEM_LOG_FILE.write_text("", encoding="utf-8")
    except Exception:
        pass
    return {"ok": True}


@router.get("/api/learning/playbook")
def api_learning_playbook():
    return get_playbook_stats()


@router.get("/api/learning/policy-feedback")
def api_learning_policy_feedback():
    return get_policy_feedback()


@router.get("/api/learning/candidates")
def api_learning_candidates(
    status: str = "candidate",
    impact_type: str = "",
    source: str = "",
    limit: int = 50,
):
    safe_limit = max(1, min(int(limit or 50), 200))
    candidates = get_learning_candidates(
        status=status,
        impact_type=impact_type,
        source=source,
        max_items=safe_limit,
    )
    _schedule_learning_candidate_finalization(max_items=safe_limit)
    return {
        "candidates": candidates
    }


@router.post("/api/learning/candidates/review")
def api_learning_candidate_review(payload: _LearningCandidateReviewRequest):
    result = review_learning_candidate(
        candidate_id=payload.candidate_id,
        approved=bool(payload.approved),
        review_reason=payload.reason,
        reviewer=payload.reviewer,
    )
    if not result:
        raise HTTPException(status_code=404, detail="Learning candidate not found")

    candidate = dict(result.get("candidate") or {})
    rule = result.get("rule") if isinstance(result.get("rule"), dict) else None
    impact_type = str(candidate.get("impact_type") or "")
    if payload.approved and impact_type == "prompt_rule":
        invalidate_agent_system_cache()

    log_system_event(
        "learning",
        (
            f"Approved learning candidate {payload.candidate_id[:8]}"
            if payload.approved
            else f"Rejected learning candidate {payload.candidate_id[:8]}"
        ),
        {
            "candidate_id": str(candidate.get("id") or "")[:80],
            "impact_type": impact_type[:80],
            "status": str(candidate.get("status") or "")[:40],
            "reason": str(candidate.get("review_reason") or "")[:120],
            "reviewer": str(candidate.get("review_source") or "")[:80],
            "rule_id": str((rule or {}).get("id") or candidate.get("promoted_rule_id") or "")[:80],
            "task_type": str(candidate.get("task_type") or "")[:80],
            "provider_scope": str(candidate.get("provider_scope") or "")[:80],
            "time_filter": str(candidate.get("time_filter") or "")[:80],
        },
    )
    return result


@router.get("/api/wiki/status")
def api_wiki_status():
    return get_wiki_status()


@router.get("/api/wiki/drafts")
def api_wiki_drafts(status: str = "", limit: int = 50):
    return {"drafts": list_wiki_drafts(status=status, limit=limit)}


@router.get("/api/wiki/drafts/{draft_id}")
def api_wiki_draft_detail(draft_id: str):
    draft = get_wiki_draft(draft_id)
    if not draft:
        raise HTTPException(status_code=404, detail="Wiki draft not found")
    return draft


@router.delete("/api/wiki/drafts/{draft_id}")
def api_wiki_draft_delete(draft_id: str):
    draft = delete_wiki_draft(draft_id)
    if not draft:
        raise HTTPException(status_code=404, detail="Wiki draft not found")
    log_system_event(
        "wiki",
        "Deleted wiki memory entry",
        {
            "draft_id": str(draft.get("id") or "")[:80],
            "target_page": str(draft.get("target_page") or draft.get("published_page") or "")[:160],
        },
    )
    return {"ok": True, "deleted": draft}


@router.post("/api/wiki/drafts/{draft_id}/review")
def api_wiki_draft_review(draft_id: str, payload: _WikiDraftReviewRequest):
    draft = review_wiki_draft(
        draft_id=draft_id,
        approved=bool(payload.approved),
        review_reason=payload.reason,
        reviewer=payload.reviewer,
    )
    if not draft:
        raise HTTPException(status_code=404, detail="Wiki draft not found")
    log_system_event(
        "wiki",
        "Approved wiki draft" if payload.approved else "Rejected wiki draft",
        {
            "draft_id": str(draft.get("id") or "")[:80],
            "status": str(draft.get("status") or "")[:40],
            "target_page": str(draft.get("target_page") or "")[:160],
            "reviewer": str(draft.get("review_source") or "")[:80],
        },
    )
    return draft


@router.post("/api/wiki/drafts/{draft_id}/publish")
def api_wiki_draft_publish(draft_id: str, payload: _WikiDraftPublishRequest):
    draft = publish_wiki_draft(
        draft_id=draft_id,
        reviewer=payload.reviewer,
        title=payload.title,
        target_page=payload.target_page,
    )
    if not draft:
        raise HTTPException(status_code=404, detail="Wiki draft not found or cannot be published")
    log_system_event(
        "wiki",
        "Published wiki draft",
        {
            "draft_id": str(draft.get("id") or "")[:80],
            "target_page": str(draft.get("target_page") or draft.get("published_page") or "")[:160],
            "reviewer": str(draft.get("review_source") or "")[:80],
        },
    )
    return draft