from unittest.mock import MagicMock

from db.collection_landing_pages import (
    canonical_category,
    canonical_collection,
    collection_path_slug,
)
from db.collection_sku_mapping import (
    ACTION_EXCLUDE,
    ACTION_INCLUDE,
    MATCH_COLLECTION_REGISTRY,
    MATCH_EXPLICIT_MEMBERSHIP,
    MATCH_MANUAL_INCLUDE,
    MATCH_MASTER_COLLECTION,
    MATCH_SKU_PREFIX,
    default_style_code_for_collection,
    infer_sku_prefixes_for_collection,
    list_prefixes_from_json,
    master_style_code_for_sku,
    prefixes_to_json,
    sku_matches_prefixes,
)


def test_infer_sku_prefixes_anna_snow_white():
    assert infer_sku_prefixes_for_collection("Anna Snow White") == ["ASW"]


def test_infer_sku_prefixes_normalizes_grey():
    assert infer_sku_prefixes_for_collection("Anna Stone Grey") == ["ASG"]


def test_default_style_code_belmont():
    assert default_style_code_for_collection("Belmont Drift White") == "BDW"


def test_sku_matches_prefixes():
    assert sku_matches_prefixes("ASW-B15", ["ASW"]) is True
    assert sku_matches_prefixes("ASW-B15", ["ASG"]) is False
    assert sku_matches_prefixes("HPW-BBC39/42L/R", ["HPW"]) is True


def test_master_style_code_for_sku():
    assert master_style_code_for_sku("ASW-BBC39/42L/R") == "ASW"
    assert not master_style_code_for_sku("PLAIN")


def test_list_prefixes_from_json():
    assert list_prefixes_from_json({"items": ["asw", "HPW"]}) == ["ASW", "HPW"]
    assert list_prefixes_from_json("ASW, HPW") == ["ASW", "HPW"]
    assert list_prefixes_from_json("ASW\nHPW") == ["ASW", "HPW"]


def test_prefixes_to_json():
    assert prefixes_to_json(["asw"]) == {"items": ["ASW"]}
    assert prefixes_to_json([]) is None


def test_collection_path_slug_uses_direct_top_category_child():
    assert (
        collection_path_slug("Kitchen Cabinet", "Anna Stone Grey")
        == "kitchen-cabinets/anna-stone-gray"
    )


def test_canonical_collection_normalizes_grey_to_gray():
    assert canonical_collection("Anna Stone Grey") == "Anna Stone Gray"


def test_canonical_category_normalizes_panel_label():
    assert canonical_category("Panels & Fillers") == "Panels and Fillers"


def test_sql_sku_prefix_match_builds_prefix_likes():
    from sqlalchemy.dialects import postgresql

    from db.collection_sku_mapping import _sql_sku_prefix_match

    clause = _sql_sku_prefix_match(["ASW", "HPW"])
    assert clause is not None
    compiled = str(
        clause.compile(
            dialect=postgresql.dialect(),
            compile_kwargs={"literal_binds": True},
        )
    )
    assert "ASW-" in compiled.upper()
    assert "HPW-" in compiled.upper()
    assert "LIKE" in compiled.upper()


def test_matching_stmt_includes_listing_and_manual_skus(monkeypatch):
    from sqlalchemy.dialects import postgresql
    from unittest.mock import MagicMock

    from db.collection_sku_mapping import _matching_master_products_stmt

    monkeypatch.setattr(
        "db.master_taxonomy_merge.collection_names_for_path_slug",
        lambda session, path_slug, fallback_collection=None: ["anna stone gray"],
    )

    stmt = _matching_master_products_stmt(
        MagicMock(),
        category_l1="Kitchen Cabinets",
        collection="Anna Stone Gray",
        path_slug="kitchen-cabinets/anna-stone-gray",
        sku_prefixes=["ASG"],
        listing_skus={"LIST-ONLY-1"},
        manual_includes={"MANUAL-1"},
        search="wall",
    )
    sql = str(stmt.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}))
    assert "LIST-ONLY-1" in sql
    assert "MANUAL-1" in sql
    assert "ASG-" in sql.upper()
    assert "wall" in sql.lower()
    assert "LIMIT" not in sql.upper()


def test_discover_collection_skus_uses_sql_matcher(monkeypatch):
    from db.collection_sku_mapping import discover_collection_skus

    captured = {}

    def fake_match(session, **kwargs):
        captured.update(kwargs)
        return []

    monkeypatch.setattr("db.collection_sku_mapping._resolve_context", lambda *args, **kwargs: {
        "path_slug": "kitchen-cabinets/anna-stone-gray",
        "category_l1": "Kitchen Cabinets",
        "collection": "Anna Stone Gray",
        "sku_prefixes": ["ASG"],
    })
    monkeypatch.setattr("db.collection_sku_mapping._load_overrides", lambda *args, **kwargs: {})
    monkeypatch.setattr("db.collection_sku_mapping._listing_path_skus", lambda *args, **kwargs: set())
    monkeypatch.setattr("db.collection_sku_mapping._matching_master_products_for_collection", fake_match)
    monkeypatch.setattr("db.collection_sku_mapping.resolve_collection_skus", lambda *args, **kwargs: {
        "skus": [],
        "sku_count": 0,
    })

    discover_collection_skus(MagicMock(), path_slug="kitchen-cabinets/anna-stone-gray", limit=25)

    assert captured["collection"] == "Anna Stone Gray"
    assert captured["sku_prefixes"] == ["ASG"]
    assert "limit" not in captured


def test_resolve_collection_skus_uses_sql_matcher(monkeypatch):
    from unittest.mock import MagicMock

    from db.collection_sku_mapping import MATCH_MASTER_COLLECTION, resolve_collection_skus

    product = MagicMock()
    product.sku = "ASG-B15"
    product.collection = "Anna Stone Gray"

    monkeypatch.setattr("db.collection_sku_mapping._resolve_context", lambda *args, **kwargs: {
        "path_slug": "kitchen-cabinets/anna-stone-gray",
        "category_l1": "Kitchen Cabinets",
        "collection": "Anna Stone Gray",
        "sku_prefixes": ["ASG"],
    })
    monkeypatch.setattr("db.collection_sku_mapping._load_overrides", lambda *args, **kwargs: {})
    monkeypatch.setattr("db.collection_sku_mapping._listing_path_skus", lambda *args, **kwargs: set())
    monkeypatch.setattr(
        "db.collection_sku_mapping._matching_master_products_for_collection",
        lambda *args, **kwargs: [product],
    )
    monkeypatch.setattr("db.collection_sku_mapping._master_collection_match", lambda *args, **kwargs: True)

    result = resolve_collection_skus(MagicMock(), path_slug="kitchen-cabinets/anna-stone-gray")

    assert result["sku_count"] == 1
    assert result["skus"][0]["master_sku"] == "ASG-B15"
    assert result["skus"][0]["match_source"] in {MATCH_MASTER_COLLECTION, "sku_prefix"}


def test_matching_stmt_includes_collection_registry_id(monkeypatch):
    from sqlalchemy.dialects import postgresql
    from unittest.mock import MagicMock

    from db.collection_sku_mapping import _matching_master_products_stmt

    monkeypatch.setattr(
        "db.master_taxonomy_merge.collection_names_for_path_slug",
        lambda session, path_slug, fallback_collection=None: ["anna stone gray"],
    )

    stmt = _matching_master_products_stmt(
        MagicMock(),
        category_l1="Kitchen Cabinets",
        collection="Anna Stone Gray",
        path_slug="kitchen-cabinets/anna-stone-gray",
        sku_prefixes=[],
        collection_registry_id=42,
    )
    sql = str(stmt.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}))
    assert "collection_registry_id" in sql
    assert "42" in sql


def test_resolve_collection_skus_prefers_registry_match(monkeypatch):
    from unittest.mock import MagicMock

    from db.collection_sku_mapping import resolve_collection_skus

    product = MagicMock()
    product.sku = "ASG-B15"
    product.collection = "Other"
    product.collection_registry_id = 7

    monkeypatch.setattr("db.collection_sku_mapping._resolve_context", lambda *args, **kwargs: {
        "path_slug": "kitchen-cabinets/anna-stone-gray",
        "category_l1": "Kitchen Cabinets",
        "collection": "Anna Stone Gray",
        "sku_prefixes": [],
        "collection_registry_id": 7,
    })
    monkeypatch.setattr("db.collection_sku_mapping._load_overrides", lambda *args, **kwargs: {})
    monkeypatch.setattr("db.collection_sku_mapping._listing_path_skus", lambda *args, **kwargs: set())
    monkeypatch.setattr(
        "db.collection_sku_mapping._matching_master_products_for_collection",
        lambda *args, **kwargs: [product],
    )
    monkeypatch.setattr("db.collection_sku_mapping._master_collection_match", lambda *args, **kwargs: False)

    result = resolve_collection_skus(MagicMock(), path_slug="kitchen-cabinets/anna-stone-gray")

    assert result["sku_count"] == 1
    assert result["skus"][0]["match_source"] == MATCH_COLLECTION_REGISTRY


def test_matching_stmt_includes_explicit_membership_skus(monkeypatch):
    from sqlalchemy.dialects import postgresql
    from unittest.mock import MagicMock

    from db.collection_sku_mapping import _matching_master_products_stmt

    monkeypatch.setattr(
        "db.master_taxonomy_merge.collection_names_for_path_slug",
        lambda session, path_slug, fallback_collection=None: ["anna snow white"],
    )

    stmt = _matching_master_products_stmt(
        MagicMock(),
        category_l1="Kitchen Cabinets",
        collection="Anna Snow White",
        path_slug="kitchen-cabinets/anna-snow-white",
        sku_prefixes=[],
        explicit_member_skus={"PB-PW-BF3"},
    )
    sql = str(stmt.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}))
    assert "PB-PW-BF3" in sql


def test_resolve_collection_skus_prefers_explicit_membership(monkeypatch):
    from db.collection_sku_mapping import resolve_collection_skus

    product = MagicMock()
    product.sku = "PB-PW-BF3"
    product.collection = "Harriet Pearl White"
    product.collection_registry_id = None
    product.membership_mode = "shared"

    monkeypatch.setattr("db.collection_sku_mapping._resolve_context", lambda *args, **kwargs: {
        "path_slug": "kitchen-cabinets/anna-snow-white",
        "category_l1": "Kitchen Cabinets",
        "collection": "Anna Snow White",
        "sku_prefixes": [],
        "collection_registry_id": None,
    })
    monkeypatch.setattr("db.collection_sku_mapping._load_overrides", lambda *args, **kwargs: {})
    monkeypatch.setattr("db.collection_sku_mapping._listing_path_skus", lambda *args, **kwargs: set())
    monkeypatch.setattr(
        "db.collection_sku_mapping._explicit_member_skus",
        lambda *args, **kwargs: {"PB-PW-BF3"},
    )
    monkeypatch.setattr(
        "db.collection_sku_mapping._matching_master_products_for_collection",
        lambda *args, **kwargs: [product],
    )
    monkeypatch.setattr("db.collection_sku_mapping._master_collection_match", lambda *args, **kwargs: False)

    result = resolve_collection_skus(MagicMock(), path_slug="kitchen-cabinets/anna-snow-white")

    assert result["sku_count"] == 1
    assert result["skus"][0]["match_source"] == MATCH_EXPLICIT_MEMBERSHIP


def test_resolve_collection_skus_ignores_explicit_membership_for_native_products(monkeypatch):
    from db.collection_sku_mapping import resolve_collection_skus

    product = MagicMock()
    product.sku = "PB-PW-BF3"
    product.collection = "Harriet Pearl White"
    product.collection_registry_id = None
    product.membership_mode = "native"

    monkeypatch.setattr("db.collection_sku_mapping._resolve_context", lambda *args, **kwargs: {
        "path_slug": "kitchen-cabinets/anna-snow-white",
        "category_l1": "Kitchen Cabinets",
        "collection": "Anna Snow White",
        "sku_prefixes": [],
        "collection_registry_id": None,
    })
    monkeypatch.setattr("db.collection_sku_mapping._load_overrides", lambda *args, **kwargs: {})
    monkeypatch.setattr("db.collection_sku_mapping._listing_path_skus", lambda *args, **kwargs: set())
    monkeypatch.setattr(
        "db.collection_sku_mapping._explicit_member_skus",
        lambda *args, **kwargs: {"PB-PW-BF3"},
    )
    monkeypatch.setattr(
        "db.collection_sku_mapping._matching_master_products_for_collection",
        lambda *args, **kwargs: [product],
    )
    monkeypatch.setattr("db.collection_sku_mapping._master_collection_match", lambda *args, **kwargs: False)

    result = resolve_collection_skus(MagicMock(), path_slug="kitchen-cabinets/anna-snow-white")

    assert result["sku_count"] == 0

