"""
Database connection helpers.

These helpers are intentionally small and "best effort":
- A successful write must not 500 solely because post-write serialization triggers extra DB reads
  on a stale/closed connection (common with PgBouncer transaction pooling).
"""

from __future__ import annotations

import logging
from typing import Any, Optional

from django.db import connection
from django.db.utils import DatabaseError, OperationalError

logger = logging.getLogger(__name__)


def _ensure_usable_connection(*, log: Optional[logging.Logger] = None, extra: Optional[dict[str, Any]] = None) -> bool:
    """
    Ensure we have a usable DB connection (PgBouncer transaction pooling tolerant).

    Behavior:
    - If a connection exists but is not usable, close it.
    - Ensure a connection.
    - On OperationalError/DatabaseError, retry once (close + ensure_connection).

    Returns:
        bool: True if connection is usable/ensured, else False.
    """
    log = log or logger
    extra = extra or {}

    try:
        if connection.connection is not None and not connection.is_usable():
            connection.close()
        connection.ensure_connection()
        return True
    except (OperationalError, DatabaseError):
        # One retry
        try:
            connection.close()
            connection.ensure_connection()
            return True
        except (OperationalError, DatabaseError):
            log.warning("Database connection unusable", extra=extra, exc_info=True)
            return False


def _safe_serializer_data(
    serializer: Any, *, log: Optional[logging.Logger] = None, extra: Optional[dict[str, Any]] = None
) -> Optional[Any]:
    """
    Best-effort evaluate serializer.data, returning None on DB failure.

    This is intentionally narrow: only catches OperationalError/DatabaseError so programming
    errors still surface in development, while stale DB connections degrade gracefully.
    """
    log = log or logger
    extra = extra or {}

    if not _ensure_usable_connection(log=log, extra=extra):
        return None

    try:
        return serializer.data
    except (OperationalError, DatabaseError):
        log.warning("Post-write serialization failed due to database error", extra=extra, exc_info=True)
        return None

