"""Log formatters for SAGE audit logging.

Provides JSON Lines and structured audit event formatting.
"""

import json
import logging
from datetime import UTC, datetime
from typing import Any


class JSONLFormatter(logging.Formatter):
    """JSON Lines formatter for structured log output.

    Produces one JSON object per line, suitable for log aggregation
    systems like ELK, Splunk, or CloudWatch.
    """

    def __init__(
        self,
        include_timestamp: bool = True,
        include_level: bool = True,
        extra_fields: dict[str, Any] | None = None,
    ):
        """Initialize formatter.

        Args:
            include_timestamp: Include ISO timestamp in output.
            include_level: Include log level in output.
            extra_fields: Additional fields to include in every log entry.
        """
        super().__init__()
        self.include_timestamp = include_timestamp
        self.include_level = include_level
        self.extra_fields = extra_fields or {}

    def format(self, record: logging.LogRecord) -> str:
        """Format log record as JSON Line.

        Args:
            record: Log record to format.

        Returns:
            JSON string with newline.
        """
        log_entry: dict[str, Any] = {}

        if self.include_timestamp:
            log_entry["timestamp"] = datetime.now(UTC).isoformat()

        if self.include_level:
            log_entry["level"] = record.levelname

        log_entry["logger"] = record.name
        log_entry["message"] = record.getMessage()

        # Add extra fields from formatter config
        log_entry.update(self.extra_fields)

        # Add extra fields from log record
        if hasattr(record, "audit_data"):
            log_entry.update(record.audit_data)

        # Handle standard extra fields
        for key, value in record.__dict__.items():
            if key not in {
                "name", "msg", "args", "created", "filename", "funcName",
                "levelname", "levelno", "lineno", "module", "msecs",
                "pathname", "process", "processName", "relativeCreated",
                "stack_info", "exc_info", "exc_text", "thread", "threadName",
                "message", "taskName", "audit_data",
            }:
                log_entry[key] = value

        # Add exception info if present
        if record.exc_info:
            log_entry["exception"] = {
                "type": record.exc_info[0].__name__ if record.exc_info[0] else None,
                "message": str(record.exc_info[1]) if record.exc_info[1] else None,
                "traceback": self.formatException(record.exc_info),
            }

        return json.dumps(log_entry, default=str, ensure_ascii=False)


class AuditFormatter(JSONLFormatter):
    """Specialized formatter for audit events.

    Extends JSONLFormatter with audit-specific fields and validation.
    """

    REQUIRED_AUDIT_FIELDS = {"event_type", "event_id"}

    def __init__(
        self,
        service_name: str = "sage",
        environment: str = "development",
        **kwargs: Any,
    ):
        """Initialize audit formatter.

        Args:
            service_name: Name of the service generating audit events.
            environment: Deployment environment (development, staging, production).
            **kwargs: Additional arguments passed to JSONLFormatter.
        """
        extra_fields = kwargs.pop("extra_fields", {})
        extra_fields.update({
            "service": service_name,
            "environment": environment,
        })
        super().__init__(extra_fields=extra_fields, **kwargs)
        self.service_name = service_name
        self.environment = environment

    def format(self, record: logging.LogRecord) -> str:
        """Format audit event record.

        Args:
            record: Log record containing audit data.

        Returns:
            JSON string with audit event data.
        """
        # Ensure audit_data exists
        if not hasattr(record, "audit_data"):
            record.audit_data = {}  # type: ignore[attr-defined]

        # Add audit metadata
        audit_data: dict = getattr(record, "audit_data", {})
        audit_data.setdefault("event_type", "UNKNOWN")
        audit_data.setdefault("event_id", self._generate_event_id())

        return super().format(record)

    @staticmethod
    def _generate_event_id() -> str:
        """Generate unique event ID."""
        import uuid

        return str(uuid.uuid4())
