"""Structured audit logging for authorization decisions and mutations.

Logs every authorization decision and configuration change as a JSON line
to one or more exporters.
"""

from __future__ import annotations

import logging
from pathlib import Path
from typing import Protocol

from pyauthz.audit.models import AuditEvent, MutationEvent
from pyauthz.core.decision import Decision

logger = logging.getLogger("pyauthz.audit")


class Exporter(Protocol):
    """Protocol for audit event exporters."""

    def export(self, event: AuditEvent) -> None:
        """Export a single audit event."""
        ...


class JsonExporter:
    """Exports audit events as JSON lines to a file.

    Each line is a complete JSON object representing one authorization decision
    or mutation event.

    Example:
        exporter = JsonExporter(path="logs/authz-decisions.jsonl")
    """

    def __init__(self, path: str) -> None:
        self._path = Path(path)
        self._path.parent.mkdir(parents=True, exist_ok=True)

    def export(self, event: AuditEvent | MutationEvent) -> None:
        """Append the audit event as a JSON line."""
        line = event.model_dump_json()
        with open(self._path, "a") as f:
            f.write(line + "\n")


class AuditLogger:
    """Routes authorization decisions and mutations to one or more audit exporters.

    Attach to the engine with rp.set_audit_logger(audit_logger).

    Example:
        audit = AuditLogger(exporters=[JsonExporter(path="logs/authz.jsonl")])
        rp.set_audit_logger(audit)
    """

    def __init__(self, exporters: list[Exporter] | None = None) -> None:
        self._exporters: list[Exporter] = exporters or []

    def add_exporter(self, exporter: Exporter) -> None:
        """Add an exporter to the logger."""
        self._exporters.append(exporter)

    def log(self, decision: Decision) -> None:
        """Convert a Decision to an AuditEvent and send to all exporters."""
        event = AuditEvent(
            timestamp=decision.evaluated_at,
            decision="ALLOW" if decision.allowed else "DENY",
            subject_id=decision.request.subject.id,
            subject_roles=decision.request.subject.roles,
            action=decision.request.action,
            resource_type=decision.request.resource.type,
            resource_id=decision.request.resource.id,
            matched_policies=decision.matched_policies,
            reasoning=decision.reasoning,
            duration_us=decision.duration_us,
        )

        self._emit(event)

    def log_mutation(self, event: MutationEvent) -> None:
        """Send a mutation event to all exporters."""
        self._emit(event)

    def _emit(self, event: AuditEvent | MutationEvent) -> None:
        """Send an event to all exporters."""
        for exporter in self._exporters:
            try:
                exporter.export(event)
            except Exception:
                logger.exception("Audit exporter failed: %s", type(exporter).__name__)
