"""Tests for OE normalization, category synonyms, and offer deduplication."""

from agent_samochodowy.matcher.normalize import (
    deduplicate_offers,
    expand_category_synonyms,
    is_model_token,
    is_plausible_oe,
    normalize_oe,
)
from agent_samochodowy.models import Offer


def _offer(oid: str, platform: str = "allegro", oe: list[str] | None = None) -> Offer:
    return Offer(
        offer_id=oid,
        platform=platform,
        url=f"https://example.com/{oid}",
        title=f"Offer {oid}",
        oe_numbers=oe or [],
    )


class TestNormalizeOe:
    def test_strips_spaces(self):
        assert normalize_oe("06B 115 105") == "06B115105"

    def test_strips_dashes(self):
        assert normalize_oe("51-06500-6408") == "51065006408"

    def test_strips_dots(self):
        assert normalize_oe("51.06500.6408") == "51065006408"

    def test_strips_slashes(self):
        assert normalize_oe("A/B/C") == "ABC"

    def test_uppercase(self):
        assert normalize_oe("abc123") == "ABC123"

    def test_combined(self):
        assert normalize_oe("a-b.c 123/d") == "ABC123D"


class TestModelToken:
    def test_known_model(self):
        assert is_model_token("FH13") is True

    def test_unknown_token(self):
        assert is_model_token("06B115105") is False


class TestIsPlausibleOe:
    def test_normal_oe(self):
        assert is_plausible_oe("20744939") is True

    def test_oe_with_letters(self):
        assert is_plausible_oe("81508206039") is True

    def test_rejects_vin(self):
        assert is_plausible_oe("WDB96340310075897") is False

    def test_rejects_another_vin(self):
        assert is_plausible_oe("WBAPH5C55BA123456") is False

    def test_rejects_german_postal(self):
        assert is_plausible_oe("DE59302") is False

    def test_rejects_too_short(self):
        assert is_plausible_oe("AB") is False

    def test_accepts_4_char(self):
        assert is_plausible_oe("AE48") is True

    def test_accepts_mixed_alphanumeric(self):
        assert is_plausible_oe("SB7855") is True


class TestCategorySynonymSplit:
    def test_filtr_paliwa_separate_from_filtr_powietrza(self):
        fp = set(expand_category_synonyms("filtr paliwa"))
        fpow = set(expand_category_synonyms("filtr powietrza"))
        assert fp & fpow == set()  # no overlap

    def test_filtr_oleju_separate(self):
        fo = set(expand_category_synonyms("filtr oleju"))
        fp = set(expand_category_synonyms("filtr paliwa"))
        assert fo & fp == set()

    def test_generic_filtr_is_own_group(self):
        f = expand_category_synonyms("filtr")
        assert "filtr" in f
        assert "filter" in f
        assert "luftfilter" not in f  # not in generic

    def test_pompa_wody_separate_from_pompa_paliwa(self):
        pw = set(expand_category_synonyms("pompa wody"))
        pp = set(expand_category_synonyms("pompa paliwa"))
        assert pw & pp == set()

    def test_pompa_adblue_separate(self):
        pa = set(expand_category_synonyms("pompa adblue"))
        pw = set(expand_category_synonyms("pompa wody"))
        assert pa & pw == set()

    def test_generic_pompa_fallback(self):
        p = expand_category_synonyms("pompa")
        assert "pompa" in p
        assert "pump" in p
        assert "wasserpumpe" not in p

    def test_zawias_drzwi_separate_from_drzwi(self):
        zd = set(expand_category_synonyms("zawias drzwi"))
        d = set(expand_category_synonyms("drzwi"))
        assert zd & d == set()

    def test_przedluzenie_drzwi_no_match_zawias(self):
        """'przedłużenie drzwi' should NOT expand to zawias synonyms."""
        syns = expand_category_synonyms("przedłużenie drzwi")
        assert "scharnier" not in syns
        assert "door hinge" not in syns

    def test_klocki_hamulcowe_unchanged(self):
        k = expand_category_synonyms("klocki hamulcowe")
        assert "klocki hamulcowe" in k
        assert "brake pad" in k


class TestDeduplicateOffers:
    def test_no_duplicates(self):
        offers = [_offer("1", oe=["AAA"]), _offer("2", oe=["BBB"])]
        result = deduplicate_offers(offers)
        assert len(result) == 2

    def test_same_oe_different_platforms(self):
        offers = [
            _offer("1", "allegro", oe=["AAA"]),
            _offer("2", "ebay", oe=["AAA"]),
        ]
        result = deduplicate_offers(offers)
        assert len(result) == 1
        assert result[0].offer_id == "1"

    def test_overlapping_oe_sets(self):
        offers = [
            _offer("1", oe=["AAA", "BBB"]),
            _offer("2", oe=["BBB", "CCC"]),
        ]
        result = deduplicate_offers(offers)
        assert len(result) == 1

    def test_offers_without_oe(self):
        offers = [_offer("1"), _offer("2")]
        result = deduplicate_offers(offers)
        assert len(result) == 2

    def test_duplicate_offer_id_no_oe(self):
        offers = [_offer("1"), _offer("1")]
        result = deduplicate_offers(offers)
        assert len(result) == 1
