"""Tests for ``JsonFormatter`` — Django LOGGING-compatible wrapper."""

from __future__ import annotations

import json
import logging
from typing import TYPE_CHECKING

from mgf.common.observability.json_formatter import JsonFormatter as BaseJsonFormatter

from mgf.django import JsonFormatter

if TYPE_CHECKING:
    import pytest


class TestJsonFormatter:
    def test_no_arg_construction(self) -> None:
        # Django's '()' factory syntax constructs with no kwargs.
        # The wrapper MUST accept that.
        formatter = JsonFormatter()
        assert isinstance(formatter, BaseJsonFormatter)

    def test_emits_one_line_json(self) -> None:
        formatter = JsonFormatter()
        record = logging.LogRecord(
            name="test.logger",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="hello world",
            args=(),
            exc_info=None,
        )

        output = formatter.format(record)
        # One JSON object per line.
        assert "\n" not in output
        payload = json.loads(output)
        assert payload["level"] == "INFO"
        assert payload["msg"] == "hello world"
        assert payload["logger"] == "test.logger"

    def test_redaction_applied(self) -> None:
        formatter = JsonFormatter()
        record = logging.LogRecord(
            name="test.logger",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="login",
            args=(),
            exc_info=None,
        )
        record.password = "supersecret"  # type: ignore[attr-defined]
        record.username = "alice"  # type: ignore[attr-defined]

        output = formatter.format(record)
        payload = json.loads(output)

        # The default redactor scrubs ``password``-keyed fields.
        assert "supersecret" not in output
        assert payload.get("username") == "alice"  # not scrubbed
        assert payload.get("password") != "supersecret"

    def test_django_logging_integration(self) -> None:
        # Verify Django's logging.config.dictConfig accepts the
        # formatter via the '()' factory syntax.
        import logging.config

        logging.config.dictConfig(
            {
                "version": 1,
                "disable_existing_loggers": False,
                "formatters": {
                    "json": {"()": "mgf.django.JsonFormatter"},
                },
                "handlers": {
                    "memory": {
                        "class": "logging.NullHandler",
                        "formatter": "json",
                    },
                },
                "loggers": {
                    "test.django.formatter": {
                        "handlers": ["memory"],
                        "level": "DEBUG",
                    },
                },
            }
        )
        # If we got here without raising, dictConfig accepted the
        # factory. The handler is a NullHandler (no actual emit
        # checked — we already verified format() above).
        log = logging.getLogger("test.django.formatter")
        log.info("smoke test")  # should not raise


# ---------------------------------------------------------------------------
# SH-05 / RA-06: env-var-driven extra redaction keys.
# ---------------------------------------------------------------------------


class TestExtraRedactionKeysEnvVar:
    """SH-05 / RA-06: ``MGF_DJANGO_REDACT_EXTRA_KEYS`` extends the
    default Redactor's key list without subclassing. Zero-config
    integration for consumers with project-specific secret-bearing
    field names."""

    def test_env_var_extra_key_is_redacted(
        self,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setenv(
            "MGF_DJANGO_REDACT_EXTRA_KEYS",
            "project_secret, oauth_nonce",
        )
        formatter = JsonFormatter()

        record = logging.LogRecord(
            name="test.logger",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="redaction test",
            args=(),
            exc_info=None,
        )
        record.project_secret = "TOP-SECRET-VALUE"
        record.oauth_nonce = "nonce-abc-123"
        record.public_field = "ok"

        output = json.loads(formatter.format(record))
        # Extra keys are redacted...
        assert output["project_secret"] != "TOP-SECRET-VALUE"
        assert output["oauth_nonce"] != "nonce-abc-123"
        # ...but unrelated fields are untouched.
        assert output["public_field"] == "ok"

    def test_no_env_var_uses_default_redactor(
        self,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.delenv("MGF_DJANGO_REDACT_EXTRA_KEYS", raising=False)
        formatter = JsonFormatter()

        record = logging.LogRecord(
            name="test.logger",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="default redactor",
            args=(),
            exc_info=None,
        )
        # No extras configured -> a custom field name flows through
        # unredacted (the default Redactor doesn't know about it).
        record.project_secret = "would-be-leaked"
        output = json.loads(formatter.format(record))
        assert output["project_secret"] == "would-be-leaked"

    def test_empty_env_var_uses_default_redactor(
        self,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setenv("MGF_DJANGO_REDACT_EXTRA_KEYS", "   ")
        # An empty / whitespace-only env var must behave the same as
        # unset — degrade gracefully, don't construct an empty
        # `extra_keys=frozenset()` Redactor that might differ subtly.
        formatter = JsonFormatter()
        assert isinstance(formatter, BaseJsonFormatter)
