"""JSON REST API for mobile / external chat clients.

Provides the same chat experience as the HTMX dashboard but over
a JSON interface that React Native (or any HTTP client) can consume.

Endpoints:
    POST /api/chat       — send a message, get the agent's reply
    POST /api/upload     — upload a file attachment
    GET  /api/chat/history  — retrieve recent chat history
    DELETE /api/chat/history — clear chat history
"""

import logging
import os
import uuid
from datetime import datetime
from pathlib import Path

from fastapi import APIRouter, File, HTTPException, Request, UploadFile
from fastapi.responses import JSONResponse

from config import settings
from dashboard.store import message_log
from timmy.session import chat as agent_chat

logger = logging.getLogger(__name__)

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

_UPLOAD_DIR = str(Path(settings.repo_root) / "data" / "chat-uploads")
_MAX_UPLOAD_SIZE = 50 * 1024 * 1024  # 50 MB


# ── POST /api/chat — helpers ─────────────────────────────────────────────────


async def _parse_chat_body(request: Request) -> tuple[dict | None, JSONResponse | None]:
    """Parse and validate the JSON request body.

    Returns (body, None) on success or (None, error_response) on failure.
    """
    content_length = request.headers.get("content-length")
    if content_length and int(content_length) > settings.chat_api_max_body_bytes:
        return None, JSONResponse(status_code=413, content={"error": "Request body too large"})

    try:
        body = await request.json()
    except Exception as exc:
        logger.warning("Chat API JSON parse error: %s", exc)
        return None, JSONResponse(status_code=400, content={"error": "Invalid JSON"})

    messages = body.get("messages")
    if not messages or not isinstance(messages, list):
        return None, JSONResponse(status_code=400, content={"error": "messages array is required"})

    return body, None


def _extract_user_message(messages: list[dict]) -> str | None:
    """Return the text of the last user message, or *None* if absent."""
    for msg in reversed(messages):
        if msg.get("role") == "user":
            content = msg.get("content", "")
            if isinstance(content, list):
                text_parts = [
                    p.get("text", "")
                    for p in content
                    if isinstance(p, dict) and p.get("type") == "text"
                ]
                return " ".join(text_parts).strip() or None
            text = str(content).strip()
            return text or None
    return None


def _build_context_prefix() -> str:
    """Build the system-context preamble injected before the user message."""
    now = datetime.now()
    return (
        f"[System: Current date/time is "
        f"{now.strftime('%A, %B %d, %Y at %I:%M %p')}]\n"
        f"[System: Mobile client]\n\n"
    )


def _notify_thinking_engine() -> None:
    """Record user activity so the thinking engine knows we're not idle."""
    try:
        from timmy.thinking import thinking_engine

        thinking_engine.record_user_input()
    except Exception:
        logger.debug("Failed to record user input for thinking engine")


async def _process_chat(user_msg: str) -> dict | JSONResponse:
    """Send *user_msg* to the agent, log the exchange, and return a response."""
    _notify_thinking_engine()
    timestamp = datetime.now().strftime("%H:%M:%S")

    try:
        response_text = await agent_chat(
            _build_context_prefix() + user_msg,
            session_id="mobile",
        )
        message_log.append(role="user", content=user_msg, timestamp=timestamp, source="api")
        message_log.append(role="agent", content=response_text, timestamp=timestamp, source="api")
        return {"reply": response_text, "timestamp": timestamp}

    except Exception as exc:
        error_msg = f"Agent is offline: {exc}"
        logger.error("api_chat error: %s", exc)
        message_log.append(role="user", content=user_msg, timestamp=timestamp, source="api")
        message_log.append(role="error", content=error_msg, timestamp=timestamp, source="api")
        return JSONResponse(
            status_code=503,
            content={"error": error_msg, "timestamp": timestamp},
        )


# ── POST /api/chat ────────────────────────────────────────────────────────────


@router.post("/chat")
async def api_chat(request: Request):
    """Accept a JSON chat payload and return the agent's reply.

    Request body:
        {"messages": [{"role": "user"|"assistant", "content": "..."}]}

    Response:
        {"reply": "...", "timestamp": "HH:MM:SS"}
    """
    body, err = await _parse_chat_body(request)
    if err:
        return err

    user_msg = _extract_user_message(body["messages"])
    if not user_msg:
        return JSONResponse(status_code=400, content={"error": "No user message found"})

    return await _process_chat(user_msg)


# ── POST /api/upload ──────────────────────────────────────────────────────────


@router.post("/upload")
async def api_upload(file: UploadFile = File(...)):
    """Accept a file upload and return its URL.

    Response:
        {"url": "/static/chat-uploads/...", "fileName": "...", "mimeType": "..."}
    """
    os.makedirs(_UPLOAD_DIR, exist_ok=True)

    suffix = uuid.uuid4().hex[:12]
    safe_name = os.path.basename(file.filename or "upload")
    stored_name = f"{suffix}-{safe_name}"
    file_path = os.path.join(_UPLOAD_DIR, stored_name)

    # Defense-in-depth: verify resolved path stays within upload directory
    resolved = Path(file_path).resolve()
    upload_root = Path(_UPLOAD_DIR).resolve()
    if not str(resolved).startswith(str(upload_root)):
        raise HTTPException(status_code=400, detail="Invalid file name")

    contents = await file.read()
    if len(contents) > _MAX_UPLOAD_SIZE:
        raise HTTPException(status_code=413, detail="File too large (max 50 MB)")
    with open(file_path, "wb") as f:
        f.write(contents)

    # Return a URL the mobile app can reference
    url = f"/uploads/{stored_name}"

    return {
        "url": url,
        "fileName": file.filename or "upload",
        "mimeType": file.content_type or "application/octet-stream",
    }


# ── GET /api/chat/history ────────────────────────────────────────────────────


@router.get("/chat/history")
async def api_chat_history():
    """Return the in-memory chat history as JSON."""
    return {
        "messages": [
            {
                "role": msg.role,
                "content": msg.content,
                "timestamp": msg.timestamp,
                "source": msg.source,
            }
            for msg in message_log.all()
        ]
    }


# ── DELETE /api/chat/history ──────────────────────────────────────────────────


@router.delete("/chat/history")
async def api_clear_history():
    """Clear the in-memory chat history."""
    message_log.clear()
    return {"success": True}
