"""Auth, user settings, and core agent invoke/chat/ops endpoints."""
from __future__ import annotations

import json
from typing import Any

from fastapi import APIRouter, Query, Request

from api.deps import (
    _actor,
    _auth_allow_signup,
    _effective_identity,
    _extract_bearer_token,
    _extract_metrics,
    _identity_user_id,
    _mask_secret,
    _require_role,
    _resolve_langfuse_settings_for_request,
    _resolve_thread_id,
)
from api.models import (
    AuthLoginRequest,
    AuthRegisterRequest,
    InvokeRequest,
    LangfuseProjectCreateRequest,
    LangfuseProjectUpdateRequest,
    LensQueryResponse,
    UserLangfuseSettingsRequest,
)
from src.agent_core import run
from src.user_store import (
    authenticate_user,
    count_users,
    create_user,
    create_user_langfuse_project,
    delete_user_langfuse_project,
    get_user_langfuse_project,
    get_user_langfuse_settings,
    insert_chat_message,
    issue_session,
    list_chat_messages,
    list_chat_threads,
    list_user_langfuse_projects,
    revoke_session_token,
    set_default_langfuse_project,
    update_user_langfuse_project,
    upsert_user_langfuse_settings,
)

from fastapi import HTTPException

router = APIRouter()


@router.get("/health")
def health(request: Request) -> dict[str, Any]:
    from datetime import datetime, timezone
    from api.state import BOOT_TS

    uptime = (datetime.now(timezone.utc) - BOOT_TS).total_seconds()
    return {"ok": True, "status": "ok", "uptime_seconds": int(uptime)}


@router.get("/api/auth/me")
def auth_me(request: Request) -> dict[str, Any]:
    identity = _effective_identity(request)
    data = {
        "auth_mode": identity.get("auth_mode"),
        "role": identity.get("role"),
        "scopes": identity.get("scopes"),
        "actor": identity.get("actor"),
        "token_id": identity.get("token_id"),
        "subject": identity.get("subject"),
        "user_id": identity.get("user_id"),
    }
    # Include langfuse_user_id for user-session callers
    user_id = identity.get("user_id")
    if user_id:
        from src.user_store import get_user
        user = get_user(user_id)
        if user:
            data["langfuse_user_id"] = user.get("langfuse_user_id", "")
    return {"ok": True, "data": data}


@router.post("/api/auth/register")
def auth_register(payload: AuthRegisterRequest, request: Request) -> dict[str, Any]:
    allow_signup = _auth_allow_signup()
    is_admin_creating = False
    if not allow_signup and count_users() > 0:
        _require_role(request, "super_admin")
        is_admin_creating = True
    elif count_users() > 0:
        # Even with signup enabled, check if caller is admin for role assignment
        try:
            identity = _effective_identity(request)
            caller_role = str(identity.get("role") or "")
            from api.state import ROLE_LEVEL
            is_admin_creating = ROLE_LEVEL.get(caller_role, 0) >= ROLE_LEVEL.get("super_admin", 5)
        except HTTPException:
            pass

    # Only super_admin can assign elevated roles; public signup always gets "analyst"
    role = payload.role if is_admin_creating else "analyst"
    try:
        user = create_user(
            username=payload.username,
            password=payload.password,
            role=role,
            langfuse_user_id=payload.langfuse_user_id,
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    return {"ok": True, "data": user}


@router.post("/api/auth/login")
def auth_login(payload: AuthLoginRequest) -> dict[str, Any]:
    user = authenticate_user(username=payload.username, password=payload.password)
    if user is None:
        raise HTTPException(status_code=401, detail="invalid username or password")
    session = issue_session(user_id=str(user["user_id"]), expires_hours=payload.expires_hours)
    return {
        "ok": True,
        "data": {
            "access_token": session["token"],
            "token_type": "bearer",
            "expires_at": session["expires_at"],
            "user": user,
        },
    }


@router.post("/api/auth/logout")
def auth_logout(request: Request) -> dict[str, Any]:
    _effective_identity(request)
    token = _extract_bearer_token(request)
    revoked = revoke_session_token(token)
    return {"ok": True, "data": {"revoked": bool(revoked)}}


@router.get("/api/users/me/langfuse-settings")
def user_langfuse_settings_get(request: Request) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    settings = get_user_langfuse_settings(user_id) or {
        "user_id": user_id,
        "base_url": "",
        "public_key": "",
        "secret_key": "",
        "environment": "",
        "updated_at": None,
    }
    return {
        "ok": True,
        "data": {
            "user_id": settings["user_id"],
            "base_url": settings["base_url"],
            "public_key_masked": _mask_secret(settings["public_key"]),
            "secret_key_masked": _mask_secret(settings["secret_key"]),
            "environment": settings["environment"],
            "updated_at": settings.get("updated_at"),
        },
    }


@router.put("/api/users/me/langfuse-settings")
def user_langfuse_settings_put(payload: UserLangfuseSettingsRequest, request: Request) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    existing = get_user_langfuse_settings(user_id) or {}
    base_url = str(payload.base_url or "").strip() or str(existing.get("base_url") or "").strip()
    public_key = str(payload.public_key or "").strip() or str(existing.get("public_key") or "").strip()
    secret_key = str(payload.secret_key or "").strip() or str(existing.get("secret_key") or "").strip()
    environment = str(payload.environment or "").strip() or str(existing.get("environment") or "").strip()
    try:
        result = upsert_user_langfuse_settings(
            user_id=user_id,
            base_url=base_url,
            public_key=public_key,
            secret_key=secret_key,
            environment=environment,
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    return {
        "ok": True,
        "data": {
            "user_id": result["user_id"],
            "base_url": base_url,
            "public_key_masked": _mask_secret(public_key),
            "secret_key_masked": _mask_secret(secret_key),
            "environment": environment,
            "updated_at": result.get("updated_at"),
        },
    }


# ---------------------------------------------------------------------------
# Multi-project Langfuse CRUD
# ---------------------------------------------------------------------------

@router.get("/api/users/me/langfuse-projects")
def langfuse_projects_list(request: Request) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    projects = list_user_langfuse_projects(user_id)
    safe_projects = []
    for p in projects:
        safe_projects.append({
            "id": p["id"],
            "name": p["name"],
            "base_url": p["base_url"],
            "public_key_masked": _mask_secret(p["public_key"]),
            "secret_key_masked": _mask_secret(p["secret_key"]),
            "environment": p["environment"],
            "is_default": p["is_default"],
            "created_at": p["created_at"],
            "updated_at": p["updated_at"],
        })
    return {"ok": True, "data": safe_projects}


@router.post("/api/users/me/langfuse-projects")
def langfuse_projects_create(payload: LangfuseProjectCreateRequest, request: Request) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    try:
        project = create_user_langfuse_project(
            user_id,
            name=payload.name,
            base_url=payload.base_url,
            public_key=payload.public_key,
            secret_key=payload.secret_key,
            environment=payload.environment,
            is_default=payload.is_default,
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    return {"ok": True, "data": {
        "id": project["id"],
        "name": project["name"],
        "base_url": project["base_url"],
        "public_key_masked": _mask_secret(project["public_key"]),
        "secret_key_masked": _mask_secret(project["secret_key"]),
        "environment": project["environment"],
        "is_default": project["is_default"],
        "created_at": project["created_at"],
        "updated_at": project["updated_at"],
    }}


@router.get("/api/users/me/langfuse-projects/{project_id}")
def langfuse_projects_get(project_id: int, request: Request) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    project = get_user_langfuse_project(project_id, user_id=user_id)
    if not project:
        raise HTTPException(status_code=404, detail="project not found")
    return {"ok": True, "data": {
        "id": project["id"],
        "name": project["name"],
        "base_url": project["base_url"],
        "public_key_masked": _mask_secret(project["public_key"]),
        "secret_key_masked": _mask_secret(project["secret_key"]),
        "environment": project["environment"],
        "is_default": project["is_default"],
        "created_at": project["created_at"],
        "updated_at": project["updated_at"],
    }}


@router.put("/api/users/me/langfuse-projects/{project_id}")
def langfuse_projects_update(project_id: int, payload: LangfuseProjectUpdateRequest, request: Request) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    fields = {k: v for k, v in payload.model_dump().items() if v is not None}
    project = update_user_langfuse_project(project_id, user_id=user_id, **fields)
    if not project:
        raise HTTPException(status_code=404, detail="project not found")
    return {"ok": True, "data": {
        "id": project["id"],
        "name": project["name"],
        "base_url": project["base_url"],
        "public_key_masked": _mask_secret(project["public_key"]),
        "secret_key_masked": _mask_secret(project["secret_key"]),
        "environment": project["environment"],
        "is_default": project["is_default"],
        "created_at": project["created_at"],
        "updated_at": project["updated_at"],
    }}


@router.delete("/api/users/me/langfuse-projects/{project_id}")
def langfuse_projects_delete(project_id: int, request: Request) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    deleted = delete_user_langfuse_project(project_id, user_id=user_id)
    if not deleted:
        raise HTTPException(status_code=404, detail="project not found")
    return {"ok": True, "data": {"deleted": True}}


@router.put("/api/users/me/langfuse-projects/{project_id}/default")
def langfuse_projects_set_default(project_id: int, request: Request) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    ok = set_default_langfuse_project(project_id, user_id=user_id)
    if not ok:
        raise HTTPException(status_code=404, detail="project not found")
    return {"ok": True, "data": {"is_default": True}}


@router.post("/v1/agent/invoke")
def invoke(payload: InvokeRequest, request: Request) -> dict[str, str]:
    _effective_identity(request)
    thread_id = _resolve_thread_id(payload.thread_id, prefix="invoke")
    user_id = _identity_user_id(request) or ""
    langfuse_settings = _resolve_langfuse_settings_for_request(request, project_id=payload.project_id)
    output = run(payload.message, thread_id=thread_id, user_id=user_id, langfuse_settings=langfuse_settings, model=payload.model)
    return {"output": output}


@router.post("/api/chat", response_model=LensQueryResponse)
def chat(payload: InvokeRequest, request: Request) -> LensQueryResponse:
    _effective_identity(request)
    thread_id = _resolve_thread_id(payload.thread_id, prefix="chat")
    user_id = _identity_user_id(request) or ""
    langfuse_settings = _resolve_langfuse_settings_for_request(request, project_id=payload.project_id)
    output = run(payload.message, thread_id=thread_id, user_id=user_id, langfuse_settings=langfuse_settings, model=payload.model)
    metrics = _extract_metrics(output)

    # Persist to DB
    model_str = str(payload.model or "")
    insert_chat_message(thread_id, user_id or "", "user", payload.message, model=model_str, project_id=payload.project_id)
    insert_chat_message(thread_id, user_id or "", "assistant", output, model=model_str, project_id=payload.project_id, metrics=json.dumps(metrics) if metrics else "")

    return LensQueryResponse(output=output, metrics=metrics, thread_id=thread_id)


@router.get("/api/chat/threads")
def chat_threads(
    request: Request,
    limit: int = Query(default=50, ge=1, le=200),
    offset: int = Query(default=0, ge=0),
) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    threads = list_chat_threads(user_id=user_id, limit=limit, offset=offset)
    return {"ok": True, "data": threads}


@router.get("/api/chat/threads/{thread_id}/messages")
def chat_thread_messages(
    thread_id: str,
    request: Request,
    limit: int = Query(default=100, ge=1, le=500),
) -> dict[str, Any]:
    _effective_identity(request)
    user_id = _identity_user_id(request)
    if not user_id:
        raise HTTPException(status_code=400, detail="user session required")
    # Verify thread ownership: only return messages if thread belongs to caller
    threads = list_chat_threads(user_id=user_id, limit=1000, offset=0)
    thread_ids = {t.get("thread_id") for t in threads}
    if thread_id not in thread_ids:
        raise HTTPException(status_code=403, detail="access denied")
    messages = list_chat_messages(thread_id, limit=limit)
    return {"ok": True, "data": messages}


@router.post("/api/ops/query", response_model=LensQueryResponse)
def ops_query(payload: InvokeRequest, request: Request) -> LensQueryResponse:
    _effective_identity(request)
    thread_id = _resolve_thread_id(payload.thread_id, prefix="ops")
    user_id = _identity_user_id(request) or ""
    langfuse_settings = _resolve_langfuse_settings_for_request(request, project_id=payload.project_id)
    output = run(payload.message, thread_id=thread_id, user_id=user_id, langfuse_settings=langfuse_settings, model=payload.model)
    return LensQueryResponse(output=output, metrics=_extract_metrics(output), thread_id=thread_id)
