"""HTTP routes for the first-class User Model surface (Phase 4).

Exposes the User Model and its proposal system to the SwiftUI app so the human
can view, correct, accept/reject proposals, and create checkpoints.

This is the enabling backend for the "My Model" / "About Me" trust surface.
"""

from __future__ import annotations

import logging
import os
from typing import Any

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

from core import get_workspace_path
from utils.user_model import (
    load_user_model,
    accept_proposal,
    reject_proposal,
    list_proposals,
    filter_proposals_for_you_pane,
    render_markdown,
    create_checkpoint,
    list_checkpoints,
    get_checkpoint,
    apply_patch,
)

_log = logging.getLogger(__name__)

_USER_MODEL_PATCH_MAX_BYTES = 256 * 1024


class UserModelPatch(BaseModel):
    """Partial update to the User Model (top-level sections)."""
    patch: dict[str, Any] = Field(..., description="Top-level sections to merge into the model")


class ProposalAction(BaseModel):
    """Body for accept/reject actions."""
    user_note: str | None = Field(None, max_length=2000)


class CreateCheckpointBody(BaseModel):
    label: str = Field(..., min_length=1, max_length=200)


def _workspace() -> str:
    ws = get_workspace_path()
    if not ws or not os.path.isdir(ws):
        raise HTTPException(status_code=503, detail="Workspace not available")
    return ws


def register_user_model_routes(app) -> None:
    """Register User Model routes on the FastAPI app."""
    router = APIRouter(prefix="/user_model", tags=["user_model"])

    @router.get("")
    def get_user_model():
        """Return the current User Model as JSON."""
        ws = _workspace()
        model = load_user_model(ws)
        from utils.user_model_noticing_summary import build_noticing_summary

        return {
            "model": model.to_dict(),
            "rendered_markdown": render_markdown(model),
            "noticing_summary": build_noticing_summary(ws),
        }

    @router.patch("")
    async def patch_user_model(request: Request):
        """Apply a direct patch from the user (Settings → You)."""
        raw = await request.body()
        if len(raw) > _USER_MODEL_PATCH_MAX_BYTES:
            raise HTTPException(status_code=413, detail="Patch payload too large")
        try:
            import json

            payload = json.loads(raw.decode("utf-8"))
            body = UserModelPatch(**payload)
        except Exception as exc:
            raise HTTPException(status_code=400, detail=f"Invalid patch body: {exc}") from exc
        ws = _workspace()
        result = apply_patch(ws, body.patch, actor="user")
        if not result.get("ok"):
            raise HTTPException(status_code=400, detail=result.get("error"))
        return {"ok": True, "last_updated": result.get("last_updated")}

    @router.get("/proposals")
    def get_proposals():
        """List pending proposals."""
        ws = _workspace()
        return {"proposals": filter_proposals_for_you_pane(ws)}

    @router.get("/checkpoints")
    def get_checkpoints():
        """List past checkpoints (lightweight metadata, newest first)."""
        ws = _workspace()
        return {"checkpoints": list_checkpoints(ws)}

    @router.get("/checkpoints/{checkpoint_id}")
    def get_checkpoint_detail(checkpoint_id: str):
        """Return the stored data for one specific checkpoint (including the model snapshot)."""
        ws = _workspace()
        data = get_checkpoint(ws, checkpoint_id)
        if not data:
            raise HTTPException(status_code=404, detail="Checkpoint not found")
        return data

    @router.post("/proposals/{proposal_id}/accept")
    def accept(proposal_id: str, body: ProposalAction | None = Body(default=None)):
        ws = _workspace()
        note = body.user_note if body else None
        result = accept_proposal(ws, proposal_id, user_note=note)
        if not result.get("ok"):
            raise HTTPException(status_code=400, detail=result.get("error"))
        return {"ok": True}

    @router.post("/proposals/{proposal_id}/reject")
    def reject(proposal_id: str, body: ProposalAction | None = Body(default=None)):
        ws = _workspace()
        note = body.user_note if body else None
        result = reject_proposal(ws, proposal_id, user_note=note)
        if not result.get("ok"):
            raise HTTPException(status_code=400, detail=result.get("error"))
        return {"ok": True}

    @router.post("/checkpoint")
    def create_checkpoint_route(body: CreateCheckpointBody):
        """Create a named checkpoint of the current User Model state."""
        ws = _workspace()
        result = create_checkpoint(ws, body.label, actor="ui")
        if not result.get("ok"):
            raise HTTPException(status_code=400, detail=result.get("error"))
        return result

    app.include_router(router)
    _log.info("Registered User Model routes (/user_model)")
