#!/usr/bin/env python3
"""
VPS Monitor Agent — à déployer sur chaque VPS.
Expose une API REST utilisée par le backend central pour interroger les conteneurs Docker.
"""

import os
from datetime import datetime, timezone

import docker
from docker.errors import DockerException, NotFound
from fastapi import Depends, FastAPI, HTTPException, Security
from fastapi.middleware.cors import CORSMiddleware
from fastapi.security import APIKeyHeader

# ─── Config ───────────────────────────────────────────────────────────────────

API_KEY     = os.getenv("AGENT_API_KEY", "changeme-please")
AGENT_PORT  = int(os.getenv("AGENT_PORT", "8001"))

# ─── App ──────────────────────────────────────────────────────────────────────

app = FastAPI(title="VPS Monitor Agent", version="1.0.0", docs_url=None, redoc_url=None)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["GET", "POST"],
    allow_headers=["*"],
)

api_key_header = APIKeyHeader(name="X-API-Key", auto_error=True)


def require_api_key(key: str = Security(api_key_header)) -> None:
    if key != API_KEY:
        raise HTTPException(status_code=403, detail="Clé API invalide")


def get_docker_client():
    try:
        return docker.from_env()
    except DockerException as e:
        raise HTTPException(status_code=503, detail=f"Docker inaccessible : {e}")


# ─── Routes ───────────────────────────────────────────────────────────────────

@app.get("/health")
def health():
    """Vérification de disponibilité — sans authentification."""
    return {"status": "ok", "timestamp": datetime.now(timezone.utc).isoformat()}


@app.get("/containers")
def list_containers(_: None = Depends(require_api_key)):
    """Retourne tous les conteneurs (actifs et arrêtés)."""
    client = get_docker_client()
    result = []
    for c in client.containers.list(all=True):
        image_tag = c.image.tags[0] if c.image.tags else c.image.short_id
        result.append({
            "id": c.short_id,
            "name": c.name.lstrip("/"),
            "status": c.status,
            "image": image_tag,
            "created": c.attrs.get("Created", ""),
            "compose_project": c.labels.get("com.docker.compose.project", ""),
            "compose_service": c.labels.get("com.docker.compose.service", ""),
            "ports": {
                host: [{"HostIp": b["HostIp"], "HostPort": b["HostPort"]} for b in bindings]
                for host, bindings in (c.ports or {}).items()
                if bindings
            },
        })
    return sorted(result, key=lambda x: x["name"])


@app.get("/containers/{container_id}/logs")
def get_logs(container_id: str, lines: int = 100, _: None = Depends(require_api_key)):
    """Retourne les N dernières lignes de logs d'un conteneur."""
    client = get_docker_client()
    try:
        c = client.containers.get(container_id)
        raw = c.logs(tail=lines, timestamps=True)
        return {"logs": raw.decode("utf-8", errors="replace")}
    except NotFound:
        raise HTTPException(status_code=404, detail="Conteneur introuvable")


@app.post("/containers/{container_id}/action")
def container_action(container_id: str, action: str, _: None = Depends(require_api_key)):
    """Effectue une action sur un conteneur : start, stop, restart."""
    if action not in ("start", "stop", "restart"):
        raise HTTPException(status_code=400, detail=f"Action invalide : {action}")
    client = get_docker_client()
    try:
        c = client.containers.get(container_id)
        getattr(c, action)()
        return {"status": "ok", "action": action, "container": container_id}
    except NotFound:
        raise HTTPException(status_code=404, detail="Conteneur introuvable")


# ─── Entrée ───────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=AGENT_PORT)
