"""ARIAAuditBackend — AuditBackend backed by ARIA Receipt Vault.

Implements the ``agent_os.audit_logger.AuditBackend`` protocol with
governance-grade audit export: in-memory outbox with retry,
hash chaining, idempotency keys, configurable redaction, and
dead-letter routing for persistent failures.
"""

from __future__ import annotations

import logging
from collections.abc import Callable

import httpx
from agent_os.audit_logger import AuditEntry
from authzen_backend.auth import TokenProvider

from aria_agentkit import compat as _compat  # noqa: F401 — version gate
from aria_agentkit.audit.outbox import AuditOutbox, OutboxConfig
from aria_agentkit.audit.receipt import AuditReceipt

logger = logging.getLogger(__name__)


class ARIAAuditBackend:
    """AuditBackend that exports entries to ARIA Receipt Vault.

    Satisfies the ``agent_os.audit_logger.AuditBackend`` protocol.

    Entries are redacted, hash-chained into tamper-evident receipts,
    queued in an in-memory outbox, and exported with retry/backoff.
    Unflushed entries are lost on process termination — call
    ``flush()`` or ``close()`` before shutdown.

    Args:
        receipt_vault_url: Base URL of the ARIA Receipt Vault.
        auth: TokenProvider for Receipt Vault authentication.
        http_client: Optional pre-configured httpx.Client.
        timeout: HTTP timeout in seconds.
        max_batch_size: Max receipts per export request.
        max_retries: Max export retry attempts per batch.
        redact_keys: Set of key names to redact from audit payloads.
        dead_letter_callback: Called with undeliverable receipts.
        sleep_fn: Sleep function for retry backoff. Inject a no-op
            for tests to avoid real delays.
    """

    def __init__(
        self,
        receipt_vault_url: str,
        *,
        auth: TokenProvider | None = None,
        http_client: httpx.Client | None = None,
        timeout: float = 10.0,
        max_batch_size: int = 50,
        max_retries: int = 5,
        redact_keys: frozenset[str] | None = None,
        dead_letter_callback: Callable[[list[AuditReceipt], str], None] | None = None,
        sleep_fn: Callable[[float], object] | None = None,
    ) -> None:
        self._receipt_vault_url = receipt_vault_url
        config = OutboxConfig(
            receipt_vault_url=receipt_vault_url,
            auth=auth,
            timeout=timeout,
            max_batch_size=max_batch_size,
            max_retries=max_retries,
            dead_letter_callback=dead_letter_callback,
        )
        if redact_keys is not None:
            config.redact_keys = redact_keys

        self._outbox = AuditOutbox(
            config,
            http_client=http_client,
            sleep_fn=sleep_fn,
        )

    def write(self, entry: AuditEntry) -> None:
        """Accept an AuditEntry and queue it for export."""
        payload = entry.to_dict()
        self._outbox.enqueue(payload)

    def flush(self) -> None:
        """Synchronously drain the outbox to Receipt Vault."""
        exported = self._outbox.flush()
        if exported > 0:
            logger.info("Flushed %d audit receipts to Receipt Vault", exported)

    def pending_count(self) -> int:
        """Number of entries queued but not yet exported."""
        return self._outbox.pending_count()

    def close(self) -> None:
        """Flush and release resources."""
        self._outbox.close()

    def __repr__(self) -> str:
        return f"ARIAAuditBackend(receipt_vault_url={self._receipt_vault_url!r})"

    def __enter__(self) -> ARIAAuditBackend:
        return self

    def __exit__(self, *args: object) -> None:
        self.close()
