"""Unified operations dashboard for AIF services."""
from __future__ import annotations

import os
from pathlib import Path
from typing import Any

import httpx
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles

from dashboard.clients import DashboardClients
from dashboard.config import settings

app = FastAPI(title="AIF Unified Dashboard", version="0.1.0")
clients = DashboardClients()

BASE = Path(__file__).resolve().parent
app.mount("/static", StaticFiles(directory=str(BASE / "static")), name="static")


@app.get("/")
async def dashboard_index():
    return FileResponse(str(BASE / "templates" / "index.html"))


@app.get("/health")
async def health():
    return {"status": "healthy", "name": "dashboard"}


def _error(exc: Exception) -> HTTPException:
    if isinstance(exc, httpx.HTTPStatusError):
        status_code = exc.response.status_code
        upstream_url = str(exc.request.url)
        detail: dict[str, Any] = {
            "message": f"Upstream service returned HTTP {status_code}",
            "upstream_status": status_code,
            "upstream_url": upstream_url,
        }
        try:
            detail["upstream_body"] = exc.response.json()
        except Exception:
            detail["upstream_body"] = exc.response.text

        if status_code == 404 and "/api/v1/keys" in upstream_url:
            detail["hint"] = (
                "Normbase keys API route is missing. "
                "Restart Normbase with the latest code and verify /api/v1/keys is available."
            )
        elif status_code == 401:
            detail["hint"] = "Check internal token / API key configuration."
        elif status_code == 503:
            detail["hint"] = "Upstream service unavailable. Verify dependent services are running."

        return HTTPException(status_code=502, detail=detail)

    if isinstance(exc, httpx.RequestError):
        return HTTPException(
            status_code=502,
            detail={
                "message": "Failed to connect to upstream service",
                "upstream_url": str(exc.request.url) if exc.request else None,
                "hint": "Check service URL and whether target service is running.",
            },
        )

    return HTTPException(status_code=500, detail={"message": str(exc)})


@app.get("/api/overview")
async def api_overview():
    try:
        normbase = await clients.normbase_health()
        eda = await clients.eda_health()
        mcp = await clients.mcp_health()
        recent_audit = clients.read_mcp_audit(limit=50)
        recent_errors = [
            item
            for item in recent_audit
            if item.get("ok") is False or item.get("status") in ("error", "failed")
        ][:10]
        return {
            "normbase": normbase,
            "eda": eda,
            "mcp": mcp,
            "config": {
                "normbase_base_url": settings.normbase_base_url,
                "eda_base_url": settings.eda_base_url,
                "mcp_base_url": settings.mcp_base_url,
                "mcp_audit_log_configured": bool(settings.mcp_audit_log),
            },
            "security_config": {
                "internal_service_token_configured": bool(
                    os.getenv("AIF_INTERNAL_SERVICE_TOKEN", "").strip()
                ),
                "api_key_salt_configured": bool(
                    os.getenv("AIF_API_KEY_SALT", "").strip()
                ),
            },
            "recent_errors": recent_errors,
        }
    except Exception as exc:
        raise _error(exc)


@app.get("/api/norms")
async def api_list_norms(page_size: int = 100):
    try:
        return await clients.list_norms(page_size=page_size)
    except Exception as exc:
        raise _error(exc)


@app.get("/api/norms/{norm_id}")
async def api_get_norm(norm_id: str):
    try:
        return await clients.get_norm(norm_id)
    except Exception as exc:
        raise _error(exc)


@app.post("/api/norms")
async def api_create_norm(payload: dict[str, Any]):
    try:
        return await clients.create_norm(payload)
    except Exception as exc:
        raise _error(exc)


@app.put("/api/norms/{norm_id}")
async def api_update_norm(norm_id: str, payload: dict[str, Any]):
    try:
        return await clients.update_norm(norm_id, payload)
    except Exception as exc:
        raise _error(exc)


@app.delete("/api/norms/{norm_id}")
async def api_delete_norm(norm_id: str):
    try:
        return await clients.delete_norm(norm_id)
    except Exception as exc:
        raise _error(exc)


@app.post("/api/retrieve")
async def api_retrieve(payload: dict[str, Any]):
    try:
        return await clients.retrieve_norms(payload)
    except Exception as exc:
        raise _error(exc)


@app.get("/api/agents")
async def api_list_agents(limit: int = 100):
    try:
        return await clients.list_agents(limit=limit)
    except Exception as exc:
        raise _error(exc)


@app.post("/api/agents")
async def api_create_agent(payload: dict[str, Any]):
    try:
        return await clients.create_agent(payload)
    except Exception as exc:
        raise _error(exc)


@app.get("/api/agents/{agent_id}")
async def api_get_agent(agent_id: str):
    try:
        return await clients.get_agent(agent_id)
    except Exception as exc:
        raise _error(exc)


@app.patch("/api/agents/{agent_id}")
async def api_patch_agent(agent_id: str, payload: dict[str, Any]):
    try:
        return await clients.update_agent(agent_id, payload)
    except Exception as exc:
        raise _error(exc)


@app.delete("/api/agents/{agent_id}")
async def api_delete_agent(agent_id: str):
    try:
        return await clients.delete_agent(agent_id)
    except Exception as exc:
        raise _error(exc)


@app.post("/api/agents/{agent_id}/{action}")
async def api_agent_action(agent_id: str, action: str, payload: dict[str, Any]):
    try:
        return await clients.agent_action(agent_id, action, payload)
    except Exception as exc:
        raise _error(exc)


@app.get("/api/agents/{agent_id}/traces")
async def api_get_traces(agent_id: str, limit: int = 20):
    try:
        return await clients.get_traces(agent_id, limit=limit)
    except Exception as exc:
        raise _error(exc)


@app.get("/api/mcp/info")
async def api_mcp_info():
    return {
        "tools": ["retrieve_norms", "bind_norms", "get_traces"],
        "mcp_base_url": settings.mcp_base_url,
        "auth_mode": "per-agent-key-only",
        "audit_log_configured": bool(settings.mcp_audit_log),
    }


@app.get("/api/mcp/audit")
async def api_mcp_audit(limit: int = 100):
    return {"items": clients.read_mcp_audit(limit=limit)}


@app.post("/api/mcp/rpc")
async def api_mcp_rpc(payload: dict[str, Any]):
    try:
        return await clients.mcp_rpc(payload)
    except Exception as exc:
        raise _error(exc)


@app.post("/api/keys/issue")
async def api_issue_key(payload: dict[str, Any]):
    try:
        return await clients.issue_key(payload)
    except Exception as exc:
        raise _error(exc)


@app.get("/api/keys")
async def api_list_keys(agent_id: str | None = None, include_inactive: bool = False):
    try:
        return await clients.list_keys(agent_id=agent_id, include_inactive=include_inactive)
    except Exception as exc:
        raise _error(exc)


@app.post("/api/keys/{key_id}/revoke")
async def api_revoke_key(key_id: str):
    try:
        return await clients.revoke_key(key_id)
    except Exception as exc:
        raise _error(exc)


@app.post("/api/keys/{key_id}/rotate")
async def api_rotate_key(key_id: str):
    try:
        return await clients.rotate_key(key_id)
    except Exception as exc:
        raise _error(exc)


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(
        "dashboard.main:app",
        host=settings.host,
        port=settings.port,
        reload=settings.debug,
    )

