"""Audit logging for data changes and corrections.

Provides the AuditLogger class for recording and querying data modifications
across the system, enabling traceability of data corrections.
"""

from __future__ import annotations

from typing import Any

from sqlalchemy import desc
from sqlalchemy.orm import Session

from packages.core.common.logging import get_logger
from packages.core.storage.models import AuditLog

logger = get_logger(__name__)


class AuditLogger:
    """Logger for recording and querying data changes.

    Usage::

        audit = AuditLogger()
        audit.log_change(
            session, table_name="starters", record_id="42",
            action="CORRECT", old_values={"placing": 3}, new_values={"placing": 2},
            changed_by="admin", change_reason="Manual correction from video review",
        )

    All methods are safe to call — failures are logged but never raised,
    ensuring audit logging never blocks the primary operation.
    """

    @staticmethod
    def log_change(
        session: Session,
        table_name: str,
        record_id: str,
        action: str,
        old_values: dict[str, Any] | None = None,
        new_values: dict[str, Any] | None = None,
        changed_by: str | None = None,
        change_reason: str | None = None,
    ) -> AuditLog | None:
        """Record a data change in the audit log.

        Args:
            session: Database session.
            table_name: Name of the table that was changed.
            record_id: Primary key value of the changed record (stringified).
            action: Type of change — ``INSERT``, ``UPDATE``, ``DELETE``, or ``CORRECT``.
            old_values: Snapshot of values before the change (optional).
            new_values: Snapshot of values after the change (optional).
            changed_by: Identifier of the user/system that made the change (optional).
            change_reason: Human-readable reason for the change (optional).

        Returns:
            The created AuditLog entry, or ``None`` if logging failed.
        """
        action_upper = action.upper().strip()
        if action_upper not in ("INSERT", "UPDATE", "DELETE", "CORRECT"):
            logger.warning(
                "Invalid audit action '%s' — must be INSERT, UPDATE, DELETE, or CORRECT",
                action,
            )
            return None

        try:
            entry = AuditLog(
                table_name=table_name,
                record_id=str(record_id),
                action=action_upper,
                old_values=old_values,
                new_values=new_values,
                changed_by=changed_by,
                change_reason=change_reason,
            )
            session.add(entry)
            session.flush()
            logger.debug(
                "Audit log entry created",
                extra={
                    "table_name": table_name,
                    "record_id": record_id,
                    "action": action_upper,
                },
            )
            return entry
        except Exception:
            logger.exception(
                "Failed to create audit log entry",
                extra={
                    "table_name": table_name,
                    "record_id": record_id,
                    "action": action_upper,
                },
            )
            return None

    @staticmethod
    def get_changes_for_record(
        session: Session,
        table_name: str,
        record_id: str,
    ) -> list[AuditLog]:
        """Retrieve all audit log entries for a specific record.

        Args:
            session: Database session.
            table_name: Name of the table.
            record_id: Primary key value of the record (stringified).

        Returns:
            List of AuditLog entries, newest first.
        """
        try:
            return (
                session.query(AuditLog)
                .filter(
                    AuditLog.table_name == table_name,
                    AuditLog.record_id == str(record_id),
                )
                .order_by(desc(AuditLog.created_at))
                .all()
            )
        except Exception:
            logger.exception(
                "Failed to query audit log for record",
                extra={"table_name": table_name, "record_id": record_id},
            )
            return []

    @staticmethod
    def get_recent_changes(
        session: Session,
        limit: int = 100,
    ) -> list[AuditLog]:
        """Retrieve the most recent audit log entries across all tables.

        Args:
            session: Database session.
            limit: Maximum number of entries to return (default 100).

        Returns:
            List of recent AuditLog entries, newest first.
        """
        try:
            return (
                session.query(AuditLog)
                .order_by(desc(AuditLog.created_at))
                .limit(limit)
                .all()
            )
        except Exception:
            logger.exception("Failed to query recent audit log entries")
            return []
