"""Tests for api/logging_config.py - structured logging, PII scrubbing, audit events."""

import json
import logging

from api.logging_config import (
    AuditEvent,
    StructuredFormatter,
    _scrub_dict,
    audit_log,
    correlation_id_var,
    get_correlation_id,
    get_logger,
    scrub_pii,
    set_correlation_id,
)


class TestPIIScrubbing:
    def test_scrub_email(self):
        assert "[EMAIL]" in scrub_pii("user@example.com")

    def test_scrub_email_in_text(self):
        result = scrub_pii("Contact john.doe@company.com for help")
        assert "[EMAIL]" in result
        assert "john.doe@company.com" not in result

    def test_scrub_bearer_token(self):
        result = scrub_pii("Bearer sk-abc123def456ghi789jkl012mno")
        assert "[TOKEN]" in result
        assert "sk-abc123" not in result

    def test_scrub_sk_token(self):
        result = scrub_pii("key=sk-1234567890abcdefghij")
        assert "[TOKEN]" in result

    def test_scrub_ssn(self):
        result = scrub_pii("SSN: 123-45-6789")
        assert "[SSN]" in result
        assert "123-45-6789" not in result

    def test_scrub_credit_card(self):
        result = scrub_pii("Card: 4111-1111-1111-1111")
        assert "[CARD]" in result
        assert "4111" not in result

    def test_scrub_credit_card_no_dashes(self):
        result = scrub_pii("Card: 4111111111111111")
        assert "[CARD]" in result

    def test_no_false_positive_normal_text(self):
        text = "This is a normal log message about test_function_123"
        assert scrub_pii(text) == text

    def test_no_false_positive_short_number(self):
        text = "Error code: 404"
        assert scrub_pii(text) == text

    def test_scrub_dict_recursive(self):
        data = {
            "user": "admin",
            "email": "admin@company.com",
            "nested": {
                "token": "Bearer sk-longtoken12345678901234",
            },
            "count": 42,
        }
        result = _scrub_dict(data)
        assert result["email"] == "[EMAIL]"
        assert "[TOKEN]" in result["nested"]["token"]
        assert result["count"] == 42  # non-strings preserved
        assert result["user"] == "admin"  # no PII, unchanged


class TestCorrelationId:
    def test_set_and_get(self):
        cid = set_correlation_id("test-123")
        assert cid == "test-123"
        assert get_correlation_id() == "test-123"

    def test_auto_generate(self):
        cid = set_correlation_id()
        assert cid is not None
        assert len(cid) == 8

    def test_default_none(self):
        # Reset
        correlation_id_var.set(None)
        assert get_correlation_id() is None


class TestStructuredFormatter:
    def test_json_output(self):
        formatter = StructuredFormatter()
        record = logging.LogRecord(
            name="test",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="Hello %s",
            args=("world",),
            exc_info=None,
        )
        output = formatter.format(record)
        data = json.loads(output)
        assert data["level"] == "INFO"
        assert data["message"] == "Hello world"
        assert "timestamp" in data
        assert "location" in data

    def test_correlation_id_in_log(self):
        set_correlation_id("abc12345")
        formatter = StructuredFormatter()
        record = logging.LogRecord(
            name="test",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="test",
            args=(),
            exc_info=None,
        )
        output = formatter.format(record)
        data = json.loads(output)
        assert data["correlation_id"] == "abc12345"

    def test_correlation_id_absent(self):
        correlation_id_var.set(None)
        formatter = StructuredFormatter()
        record = logging.LogRecord(
            name="test",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="test",
            args=(),
            exc_info=None,
        )
        output = formatter.format(record)
        data = json.loads(output)
        assert "correlation_id" not in data

    def test_pii_scrubbed_in_message(self):
        formatter = StructuredFormatter()
        record = logging.LogRecord(
            name="test",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="User email: user@example.com",
            args=(),
            exc_info=None,
        )
        output = formatter.format(record)
        data = json.loads(output)
        assert "[EMAIL]" in data["message"]
        assert "user@example.com" not in data["message"]

    def test_extra_fields_scrubbed(self):
        formatter = StructuredFormatter()
        record = logging.LogRecord(
            name="test",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="test",
            args=(),
            exc_info=None,
        )
        record.extra_fields = {"api_key": "sk-supersecretkey12345678901234"}  # type: ignore
        output = formatter.format(record)
        data = json.loads(output)
        assert "[TOKEN]" in data["api_key"]

    def test_exception_info(self):
        formatter = StructuredFormatter()
        try:
            raise ValueError("test error")
        except ValueError:
            import sys
            record = logging.LogRecord(
                name="test",
                level=logging.ERROR,
                pathname="test.py",
                lineno=1,
                msg="failed",
                args=(),
                exc_info=sys.exc_info(),
            )
        output = formatter.format(record)
        data = json.loads(output)
        assert "exception" in data
        assert "ValueError" in data["exception"]


class TestAuditLog:
    def test_audit_log_fields(self):
        logger = get_logger("test.audit")
        # Just verify it doesn't raise
        audit_log(
            logger,
            AuditEvent.FIX_APPLIED,
            user_id="user-1",
            org_id="org-1",
            resource_type="fix",
            resource_id="fix-123",
            details={"patch_size": 42},
        )

    def test_audit_event_constants(self):
        assert AuditEvent.AUTH_SUCCESS == "auth.success"
        assert AuditEvent.FIX_APPLIED == "fix.applied"
        assert AuditEvent.SESSION_UPLOAD == "session.upload"


class TestGetLogger:
    def test_returns_structured_logger(self):
        logger = get_logger("test.module")
        assert isinstance(logger, logging.Logger)
