"""Tests for SamplingFilter and RedactionFilter."""

from __future__ import annotations

import logging
import time

import pytest

from azure_functions_logging._filters import RedactionFilter, SamplingFilter


def _make_record(level: int = logging.INFO, msg: str = "hello") -> logging.LogRecord:
    return logging.LogRecord(
        name="test.logger",
        level=level,
        pathname=__file__,
        lineno=1,
        msg=msg,
        args=(),
        exc_info=None,
    )


# ---------------------------------------------------------------------------
# SamplingFilter tests
# ---------------------------------------------------------------------------


def test_sampling_filter_passes_records_within_rate() -> None:
    flt = SamplingFilter(rate=3, window=10.0)
    for _ in range(3):
        assert flt.filter(_make_record()) is True


def test_sampling_filter_drops_records_beyond_rate() -> None:
    flt = SamplingFilter(rate=2, window=10.0)
    assert flt.filter(_make_record()) is True
    assert flt.filter(_make_record()) is True
    assert flt.filter(_make_record()) is False


def test_sampling_filter_resets_after_window() -> None:
    flt = SamplingFilter(rate=1, window=0.05)
    assert flt.filter(_make_record()) is True
    assert flt.filter(_make_record()) is False  # exceeded within window
    time.sleep(0.06)
    assert flt.filter(_make_record()) is True  # window reset


def test_sampling_filter_always_passes_warning_and_above() -> None:
    flt = SamplingFilter(rate=1, window=10.0)
    # Exhaust the rate
    flt.filter(_make_record())
    flt.filter(_make_record())

    assert flt.filter(_make_record(logging.WARNING)) is True
    assert flt.filter(_make_record(logging.ERROR)) is True
    assert flt.filter(_make_record(logging.CRITICAL)) is True


def test_sampling_filter_invalid_rate_raises() -> None:
    with pytest.raises(ValueError, match="rate"):
        SamplingFilter(rate=0)


def test_sampling_filter_invalid_window_raises() -> None:
    with pytest.raises(ValueError, match="window"):
        SamplingFilter(rate=1, window=0.0)


# ---------------------------------------------------------------------------
# RedactionFilter tests
# ---------------------------------------------------------------------------


def test_redaction_filter_masks_default_sensitive_keys() -> None:
    flt = RedactionFilter()
    record = _make_record(msg="login")
    setattr(record, "password", "s3cr3t")
    setattr(record, "token", "tok123")

    result = flt.filter(record)

    assert result is True
    assert getattr(record, "password") == "***"
    assert getattr(record, "token") == "***"


def test_redaction_filter_leaves_non_sensitive_keys_unchanged() -> None:
    flt = RedactionFilter()
    record = _make_record(msg="safe")
    setattr(record, "user_id", "u-1")
    setattr(record, "request_id", "r-1")

    flt.filter(record)

    assert getattr(record, "user_id") == "u-1"
    assert getattr(record, "request_id") == "r-1"


def test_redaction_filter_custom_sensitive_keys() -> None:
    flt = RedactionFilter(sensitive_keys=["account_number", "ssn"])
    record = _make_record(msg="custom")
    setattr(record, "account_number", "12345678")
    setattr(record, "ssn", "999-99-9999")
    setattr(record, "user_id", "u-1")

    flt.filter(record)

    assert getattr(record, "account_number") == "***"
    assert getattr(record, "ssn") == "***"
    assert getattr(record, "user_id") == "u-1"


def test_redaction_filter_does_not_touch_standard_fields() -> None:
    flt = RedactionFilter()
    record = _make_record(msg="standard")
    original_name = record.name
    original_levelname = record.levelname

    flt.filter(record)

    assert record.name == original_name
    assert record.levelname == original_levelname


def test_redaction_filter_key_matching_is_case_insensitive() -> None:
    flt = RedactionFilter()
    record = _make_record(msg="case")
    setattr(record, "PASSWORD", "abc")
    setattr(record, "Token", "xyz")

    flt.filter(record)

    assert getattr(record, "PASSWORD") == "***"
    assert getattr(record, "Token") == "***"


def test_redaction_filter_recursively_masks_nested_dict_keys() -> None:
    flt = RedactionFilter()
    record = _make_record(msg="nested")
    setattr(
        record,
        "payload",
        {"password": "secret", "nested": {"token": "abc", "safe": "ok"}},
    )

    flt.filter(record)

    assert getattr(record, "payload") == {
        "password": "***",
        "nested": {"token": "***", "safe": "ok"},
    }


def test_redaction_filter_recursively_masks_deeply_nested_dict_keys() -> None:
    flt = RedactionFilter()
    record = _make_record(msg="deep")
    setattr(
        record,
        "context",
        {"level_1": {"level_2": {"authorization": "Bearer x", "value": 42}}},
    )

    flt.filter(record)

    assert getattr(record, "context") == {
        "level_1": {"level_2": {"authorization": "***", "value": 42}}
    }


def test_redaction_filter_recursively_masks_dicts_inside_lists() -> None:
    flt = RedactionFilter()
    record = _make_record(msg="list")
    setattr(
        record,
        "events",
        [{"token": "abc"}, {"safe": "value"}, {"authorization": "Bearer y"}],
    )

    flt.filter(record)

    assert getattr(record, "events") == [
        {"token": "***"},
        {"safe": "value"},
        {"authorization": "***"},
    ]


def test_redaction_filter_recursively_masks_mixed_nested_structures() -> None:
    flt = RedactionFilter()
    record = _make_record(msg="mixed")
    setattr(
        record,
        "metadata",
        {
            "items": [
                {"secret": "s-1", "nested": [{"api_key": "k-1"}, {"safe": "ok"}]},
                "raw",
            ],
            "profile": {"passwd": "p-1", "name": "alice"},
        },
    )

    flt.filter(record)

    assert getattr(record, "metadata") == {
        "items": [
            {"secret": "***", "nested": [{"api_key": "***"}, {"safe": "ok"}]},
            "raw",
        ],
        "profile": {"passwd": "***", "name": "alice"},
    }
