"""Capture centralisée et expurgée des écritures HTTP."""
import json
import re

from flask import current_app, request
from flask_login import current_user

from ..extensions import db
from .models.audit import AuditLog


SENSITIVE_MARKERS = ("password", "mot_de_passe", "secret", "token", "api_key", "apikey", "pin")


def _redacted_payload():
    data = request.get_json(silent=True) if request.is_json else request.form.to_dict(flat=False)
    if not isinstance(data, dict):
        return None
    clean = {}
    for key, value in data.items():
        clean[key] = "[MASQUÉ]" if any(marker in key.lower() for marker in SENSITIVE_MARKERS) else value
    encoded = json.dumps(clean, ensure_ascii=False, default=str)
    return encoded[:8000]


def _entity_from_path():
    endpoint = request.endpoint or ""
    blueprint = endpoint.split(".", 1)[0] or None
    match = re.search(r"/(\d+)(?:/|$)", request.path)
    return blueprint, match.group(1) if match else None


def record_mutation(response):
    if request.method in {"GET", "HEAD", "OPTIONS"} or response.status_code >= 500:
        return response
    entity_type, entity_id = _entity_from_path()
    values = {
        "user_id": current_user.id if current_user.is_authenticated else None,
        "username": current_user.username if current_user.is_authenticated else "anonyme",
        "action": request.method,
        "endpoint": request.endpoint,
        "path": request.path[:500],
        "entity_type": entity_type,
        "entity_id": entity_id,
        "payload": _redacted_payload(),
        "status_code": response.status_code,
        "ip_address": (request.headers.get("X-Forwarded-For", request.remote_addr or "").split(",")[0].strip())[:64],
    }
    try:
        # Transaction séparée : le journal ne valide jamais par accident des
        # changements laissés en attente par une vue métier.
        with db.engine.begin() as connection:
            connection.execute(AuditLog.__table__.insert().values(**values))
    except Exception:
        # Une panne du journal ne doit pas transformer une réponse métier déjà
        # produite en erreur 500 ; l'erreur reste visible dans les logs serveur.
        current_app.logger.exception("Impossible d'enregistrer l'opération dans le journal d'audit")
    return response
