"""Tests for the Cookie-Inventory fuzzy-matcher.

Regression: BMW-Mail zeigte 0 OK obwohl 31 Browser-Cookies + 738
deklarierte vorhanden waren. Ursache: exact-string-match scheitert
bei `_ga` (declared) vs `_ga_K8YL3M9T` (browser).
"""

from compliance.services.mail_render_v2._cookie_inventory import (
    _matches,
    _norm_for_match,
    build_cookie_inventory,
)


class TestNormForMatch:
    def test_strip_wildcard(self):
        assert _norm_for_match("_ga*") == "_ga"

    def test_strip_regex_wildcard(self):
        assert _norm_for_match("_pk_id.*") == "_pk_id"

    def test_strip_placeholder(self):
        assert _norm_for_match("session_<id>") == "session_"

    def test_lowercase(self):
        assert _norm_for_match("__CF_BM") == "__cf_bm"


class TestMatches:
    def test_exact(self):
        assert _matches("_ga", "_ga")

    def test_declared_prefix_of_browser(self):
        # declared "_ga" matches browser "_ga_k8yl3m9t"
        assert _matches("_ga", "_ga_k8yl3m9t")

    def test_browser_prefix_of_declared(self):
        # browser "__cf_bm" matches declared "__cf_bm_session"
        assert _matches("__cf_bm_session", "__cf_bm")

    def test_short_prefix_rejected(self):
        # 2-char prefix would match too much
        assert not _matches("_g", "_ga_k8yl3m9t")

    def test_unrelated(self):
        assert not _matches("_ga", "intercom-session")


class TestBuildInventory:
    def _make_state(self, declared_cookies, browser_cookies):
        return {
            "cmp_vendors": [{
                "name": "Test", "country": "DE", "source": "dse",
                "cookies": [{"name": n} for n in declared_cookies],
            }],
            "banner_result": {
                "cookies_detailed": [{"name": n} for n in browser_cookies],
            },
            "cookie_audit": {},
        }

    def test_no_match_no_ok(self):
        rows, summary = build_cookie_inventory(
            self._make_state(["foo"], ["bar"]),
        )
        assert summary["ok"] == 0
        assert summary["orph"] == 1
        assert summary["undoc"] == 1

    def test_exact_match_yields_ok(self):
        rows, summary = build_cookie_inventory(
            self._make_state(["_ga"], ["_ga"]),
        )
        assert summary["ok"] == 1
        assert summary["orph"] == 0
        assert summary["undoc"] == 0

    def test_prefix_match_yields_ok_no_double_count(self):
        # Realistic BMW case: declared "_ga", browser "_ga_K8YL3M9T"
        rows, summary = build_cookie_inventory(
            self._make_state(["_ga"], ["_ga_K8YL3M9T"]),
        )
        assert summary["ok"] == 1, "fuzzy prefix-match should land in OK"
        assert summary["orph"] == 0, "declared must not double-count as ORPH"
        assert summary["undoc"] == 0, (
            "browser cookie must fold into declared row, not appear separately"
        )

    def test_wildcard_match(self):
        rows, summary = build_cookie_inventory(
            self._make_state(["_pk_id.*"], ["_pk_id.5"]),
        )
        assert summary["ok"] == 1

    def test_realistic_bmw_pattern(self):
        # Declared: long list with common cookies
        decl = ["_ga", "_gid", "__cf_bm", "AMP_TOKEN", "_fbp",
                "intercom-session", "_pk_id.*", "OptanonConsent"]
        # Browser: actual cookies with runtime suffixes
        bro = ["_ga_K8YL3M9T", "_gid_xyz", "__cf_bm_actual_hash",
                "AMP_TOKEN_runtime", "_fbp_123",
                "intercom-session-2026", "_pk_id.5.7d8", "OptanonConsent"]
        rows, summary = build_cookie_inventory(
            self._make_state(decl, bro),
        )
        # All 8 browser cookies should fold into the 8 declared rows.
        assert summary["ok"] == 8, f"expected 8 OK, got {summary}"
        assert summary["orph"] == 0
        assert summary["undoc"] == 0
