from db.channel_exports import (
    _apply_canonical_shopping_field_overrides,
    build_channel_product_payloads,
    compose_canonical_fields,
    enrich_catalog_attributes,
    flatten_export_bundle,
    map_fields_to_channel,
    payload_hash,
    resolve_pipeline_channel_code,
    select_price,
)
from db.canonical_attributes import composite_attribute_codes, derive_composite_attribute_value
from db.catalog_normalization_service import upsert_collection_commerce_profile
from db.models import MasterLocationRegistry, MasterProduct, MasterProductAttributeValue, MasterProductLocationAvailability
from db.master_overrides import merge_override_rows
from magento.payload_mapper import snapshot_to_magento_product
from shopify.catalog import fetch_remote_sku_map, fetch_remote_sku_map_for_skus
from shopify.product_sync import map_payload_to_product_set_input


def test_resolve_pipeline_channel_code_from_connection_identity():
    assert resolve_pipeline_channel_code("magento-1", channel_type="magento") == "magento"
    assert resolve_pipeline_channel_code("portandbell", channel_type="shopify") == "shopify"
    assert resolve_pipeline_channel_code("shopify") == "shopify"


def test_override_merge_channel_beats_global():
    rows = [
        {"sku": "A1", "channel_code": "", "attribute_code": "meta_title", "value": "Global title"},
        {"sku": "A1", "channel_code": "shopify", "attribute_code": "meta_title", "value": "Shopify title"},
        {"sku": "A1", "channel_code": "", "attribute_code": "meta_description", "value": "Global desc"},
        {"sku": "B2", "channel_code": "magento", "attribute_code": "meta_title", "value": "Not for shopify"},
    ]
    merged = merge_override_rows(rows, "shopify")

    assert merged["A1"]["meta_title"] == "Shopify title"
    assert merged["A1"]["meta_description"] == "Global desc"
    # Rows for other channels are pre-filtered by the query; merge only sees '' and the channel,
    # so a magento row passed in is ignored for shopify.
    assert "B2" not in merged


def test_compose_canonical_fields_override_wins_and_can_add_new_codes():
    core = {"title": "Master Name", "brand": "Acme", "status": "active"}
    attrs = {"cabinet_door_style": "Shaker", "title": "Attr should not exist"}
    overrides = {"title": "SEO Optimized Name", "meta_description": "Buy the best shaker cabinet"}

    fields, applied = compose_canonical_fields(core, attrs, overrides)

    assert fields["title"] == "SEO Optimized Name"
    assert fields["brand"] == "Acme"
    assert fields["cabinet_door_style"] == "Shaker"
    assert fields["meta_description"] == "Buy the best shaker cabinet"
    assert applied == {"title", "meta_description"}


def test_compose_canonical_fields_normalizes_storefront_filter_attributes():
    fields, _ = compose_canonical_fields(
        {
            "title": "B12",
            "assembly_type": "RTA",
            "stocked": "No",
        },
        {
            "grab_n_go": "Yes",
            "cabinet_door_style": "raised panel",
            "cabinet_construction_type": "Framed Construction",
            "cabinet_door_overlay": "Full Overlay",
            "collection_color": "Stone Gray",
        },
        {},
    )

    assert fields["assembly_type"] == "Ready-to-Assemble"
    assert fields["item_availability"] == "Grab & Go"
    assert fields["cabinet_door_style"] == "Raised Panel"
    assert fields["door_style"] == "Raised Panel"
    assert fields["cabinet_construction"] == "Framed"
    assert fields["cabinet_door_overlay"] == "Full Overlay"
    assert fields["color"] == "Grey"


def test_build_channel_product_payloads_exposes_location_aware_availability(catalog_intent_session):
    session = catalog_intent_session
    from unittest.mock import patch

    session.add_all(
        [
            MasterLocationRegistry(code="1", name="Keyport Warehouse", location_kind="warehouse", is_active=True),
            MasterLocationRegistry(code="2", name="Bellmawr Warehouse", location_kind="warehouse", is_active=True),
            MasterLocationRegistry(code="3", name="North Bergen Warehouse", location_kind="warehouse", is_active=True),
        ]
    )
    session.flush()
    upsert_collection_commerce_profile(
        session,
        payload={
            "collection_name": "Anna Caramel Harvest",
            "category_l1": "Kitchen Cabinets",
            "default_location_code": "2",
            "allow_geolocation": True,
            "allow_manual_selection": True,
        },
    )
    locations = {
        row.code: row
        for row in session.query(MasterLocationRegistry).all()
    }
    product = MasterProduct(
        sku="ACH-B12",
        name="B12 - Anna Caramel Harvest",
        collection="Anna Caramel Harvest",
        category_l1="Kitchen Cabinets",
        row_hash="h-ach-b12",
        is_active=True,
    )
    session.add(product)
    session.flush()
    session.add_all(
        [
            MasterProductAttributeValue(
                product_id=product.id,
                sku=product.sku,
                attribute_code="grab_n_go",
                value="Yes",
            ),
            MasterProductLocationAvailability(
                product_id=product.id,
                sku=product.sku,
                location_id=locations["1"].id,
                location_code="location_keyport",
                is_available=True,
                source_value="Yes",
                source_label="test",
            ),
            MasterProductLocationAvailability(
                product_id=product.id,
                sku=product.sku,
                location_id=locations["2"].id,
                location_code="location_bellmawr",
                is_available=True,
                source_value="Available",
                source_label="test",
            ),
            MasterProductLocationAvailability(
                product_id=product.id,
                sku=product.sku,
                location_id=locations["3"].id,
                location_code="location_north_bergen",
                is_available=False,
                source_value="Special Order",
                source_label="test",
            ),
        ]
    )
    session.commit()

    with patch("db.channel_exports._attach_association_fields", lambda *args, **kwargs: None), patch(
        "db.channel_exports._attach_taxonomy_targets", lambda *args, **kwargs: None
    ):
        payloads = build_channel_product_payloads(
            session,
            "magento",
            skus=["ACH-B12"],
            only_assigned=False,
        )

    payload = payloads[0]
    fields = payload["canonical_fields"]
    assert fields["availability_by_location"] == {
        "1": "grab_and_go",
        "2": "grab_and_go",
        "3": "special_order",
    }
    assert fields["grab_and_go_location_codes"] == ["1", "2"]
    assert fields["available_location_codes"] == ["1", "2"]
    assert fields["available_non_grab_location_codes"] == []
    assert fields["special_order_location_codes"] == ["3"]
    assert fields["is_grab_and_go_any"] is True
    assert fields["is_available_any"] is True
    assert fields["is_special_order_only"] is False
    assert fields["default_location_availability_mode"] == "grab_and_go"
    assert fields["item_availability"] == "Grab & Go"
    assert fields["pim_avail_grab_go"] == "1,2"
    assert fields["pim_avail_available"] == ""
    assert fields["pim_avail_special"] == "3"
    assert fields["default_location_code"] == "2"
    assert fields["default_location_name"] == "Bellmawr Warehouse"
    assert fields["resolved_default_location_code"] == "2"
    assert fields["resolved_default_location_name"] == "Bellmawr Warehouse"
    assert fields["resolved_location_selection_strategy"] == "default_location"
    assert fields["allow_geolocation"] is True
    assert fields["allow_manual_location_selection"] is True
    assert fields["location_selection_mode"] == "manual_with_optional_geolocation"
    assert any(item["code"] == "2" for item in fields["location_directory"])


def test_compose_canonical_fields_derives_drawer_box_from_component_columns():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "drawer_box_construction": "Dovetail",
            "drawer_box_material": "Solid Wood",
            "drawer_box_bottom_material": "Plywood",
        },
        {},
    )

    assert fields["drawer_box"] == "Dovetail Solid Wood"


def test_compose_canonical_fields_derives_material_only_drawer_box_labels():
    plywood_fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {"drawer_box_material": "Plywood"},
        {},
    )
    metal_fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {"drawer_box_material": "Metal"},
        {},
    )

    assert plywood_fields["drawer_box"] == "Plywood"
    assert metal_fields["drawer_box"] == "Metal Sided"


def test_compose_canonical_fields_prefers_drawer_box_material_over_bottom_material():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "drawer_box_material": "Solid Wood",
            "drawer_box_bottom_material": "Plywood",
        },
        {},
    )

    assert fields["drawer_box"] == "Solid Wood"


def test_compose_canonical_fields_falls_back_to_bottom_material_for_drawer_box():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {"drawer_box_bottom_material": "Plywood"},
        {},
    )

    assert fields["drawer_box"] == "Plywood"


def test_compose_canonical_fields_derives_accessories_from_title_and_description():
    fields, _ = compose_canonical_fields(
        {"title": '24"W x 2.5"H x 10"D Floating Shelf'},
        {"description": "Floating shelf accessory for wall display"},
        {},
    )

    assert fields["accessories"] == "floating shelf"


def test_compose_canonical_fields_does_not_mark_standalone_accessory_skus_as_accessories():
    fields, _ = compose_canonical_fields(
        {"title": "Loose Glass Door"},
        {
            "cabinet_type_sub_category_1": "Accessories",
            "cabinet_type_sub_category_2": "Glass Door",
            "description": "Replacement loose glass door only.",
        },
        {},
    )

    assert "accessories" not in fields


def test_compose_canonical_fields_derives_angle_and_box_construction():
    fields, _ = compose_canonical_fields(
        {"title": "Blind Corner Base Cabinet - Left"},
        {
            "cabinet_construction_type": "Framed Construction",
            "variation_angle": "left hand",
        },
        {},
    )

    assert fields["angle"] == "Left"
    assert fields["box_construction"] == "Framed"


def test_compose_canonical_fields_limits_angle_to_approved_values():
    fields, _ = compose_canonical_fields(
        {"title": "Corner Wall Cabinet - Upper Right"},
        {},
        {},
    )

    assert fields["angle"] == "Upper Right"


def test_compose_canonical_fields_derives_collection_style_and_cabinet_group_details():
    fields, _ = compose_canonical_fields(
        {"title": "B12", "collection": "Anna Caramel Harvest"},
        {
            "cabinet_type_sub_category_1": "Base",
            "cabinet_type_sub_category_2": "Single Door Base Cabinet",
        },
        {},
    )

    assert fields["collection_style"] == "Anna Caramel Harvest"
    assert fields["cabinet_type"] == "base cabinets"
    assert fields["base_cabinets"] == "single door base cabinet"


def test_compose_canonical_fields_uses_product_collection_series_name_for_collection_targets():
    fields, _ = compose_canonical_fields(
        {"title": "B12", "collection": ""},
        {
            "product_collection_series_name": "Anna Caramel Harvest",
        },
        {},
    )

    assert fields["collection"] == "Anna Caramel Harvest"
    assert fields["collection_style"] == "Anna Caramel Harvest"


def test_compose_canonical_fields_derives_wall_and_panel_detail_attributes():
    wall_fields, _ = compose_canonical_fields(
        {"title": "W2430"},
        {
            "cabinet_type_sub_category_1": "Wall",
            "cabinet_type_sub_category_2": "Corner Wall Cabinet",
        },
        {},
    )
    panel_fields, _ = compose_canonical_fields(
        {"title": "BF3"},
        {
            "cabinet_type_sub_category_1": "Panels and Fillers",
            "cabinet_type_sub_category_2": "Filler Strip",
        },
        {},
    )

    assert wall_fields["wall_cabinets"] == "corner wall cabinet"
    assert panel_fields["panels_and_fillers"] == "filler strip"


def test_compose_canonical_fields_derives_remote_facing_finish_overlay_and_color_fields():
    fields, _ = compose_canonical_fields(
        {"title": "B12", "collection": "Anna Caramel Harvest"},
        {
            "cabinet_door_finish_type": "Stained Finish",
            "cabinet_door_overlay": "Full Overlay",
            "collection_color": "Anna Caramel Harvest",
            "door_hinge_type": "Concealed 6-Way Adjustable Soft Close",
            "drawer_slide_type": "Undermount Full Extension Soft Close",
        },
        {},
    )

    assert fields["color_finish"] == "Caramel Harvest"
    assert fields["finish_type"] == "Caramel Harvest Stain Finish"
    assert fields["overlay"] == "Full Overlay - Framed"
    assert fields["hinges"] == "Concealed Soft-Close Hinges"
    assert fields["drawer_slides"] == "Undermount Slides"
    assert fields["soft_close"] == "Yes"


def test_compose_canonical_fields_derives_material_and_clip_labels():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "drawer_box_material": '3/4" Solid Wood',
            "drawer_box_construction": "Dovetail Box",
            "face_frame": '3/4" x 1 1/2" Solid Wood Face Frame',
            "cabinet_shelf_clip_type": "Stainless Steel Locking",
        },
        {},
    )

    assert fields["materials_box"] == '3/4" Solid Wood Dovetail Box'
    assert fields["materials_face_frame"] == '3/4" x 1 1/2" Solid Wood Face Frame'
    assert fields["shelf_clips"] == "Stainless Steel (locking)"


def test_compose_canonical_fields_derives_drawer_slides_and_face_frame_from_master_export_names():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "drawer_slide_glide": "Undermount Full Extension Soft Close",
            "cabinet_face_frame_material": "Solid Hardwood",
        },
        {},
    )

    assert fields["drawer_slides"] == "Undermount Slides"
    assert fields["materials_face_frame"] == "Solid Wood"


def test_compose_canonical_fields_derives_text_and_boolean_remote_fields():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "number_of_shelves_included": "2",
            "product_warranty": "5 Year Warranty",
            "product_care_instructions": "Wipe clean with a soft cloth.",
        },
        {},
    )

    assert fields["shelves_included"] == "Yes"
    assert fields["warranty"] == "5 Year Warranty"
    assert fields["care_instructions"] == "Wipe clean with a soft cloth."


def test_compose_canonical_fields_builds_simple_html_content_blocks():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "cabinet_style_short_description": "Bright, crisp and modern.",
            "product_warranty": "Limited Warranty",
            "warranty_length": "5 Year",
            "package_height_in": "40",
            "package_width_in": "18",
            "package_length_in": "27",
            "package_weight_lb": "91",
            "cabinet_back_material": "Plywood",
            "cabinet_bottom_material": "Plywood",
            "cabinet_carcass_material": "Plywood",
            "cabinet_face_frame_material": "Solid Hardwood",
            "drawer_slide_glide": "Undermount Full Extension Soft Close",
            "door_hinge_type": "Concealed 6-Way Adjustable Soft Close",
            "cabinet_door_overlay": "Full Overlay",
        },
        {},
    )

    assert "<p>Bright, crisp and modern.</p>" in fields["description"]
    assert "Soft close doors and drawers help prevent slamming" in fields["description"]
    assert "<dt>Warranty</dt><dd>5 Year Limited Warranty</dd>" in fields["additional_information"]
    assert "<dt>Package Height (in)</dt><dd>40</dd>" in fields["additional_information"]
    assert "<dt>Cabinet Back Material</dt><dd>Plywood</dd>" in fields["materials"]
    assert "<dt>Materials Face Frame</dt><dd>Solid Wood</dd>" in fields["materials"]


def test_compose_canonical_fields_strips_care_instruction_boilerplate():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "product_care_instructions": "Ships as Left hinge. Can be modified in field.",
        },
        {},
    )

    assert fields["care_instructions"] == "Ships as Left hinge."


def test_compose_canonical_fields_merges_warranty_type_and_length_and_package_fields():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "product_warranty": "Limited Warranty",
            "warranty_length": "5 Year",
            "packaged_dimension_height": "34.5",
            "packaged_dimension_width": "15",
            "packaged_dimension_depth": "24",
            "packaged_gross_weight": "86",
        },
        {},
    )

    assert fields["warranty"] == "5 Year Limited Warranty"
    assert fields["packaged_dimension_height"] == "34.5"
    assert fields["packaged_dimension_width"] == "15"
    assert fields["packaged_dimension_depth"] == "24"
    assert fields["packaged_gross_weight"] == "86"


def test_compose_canonical_fields_normalizes_finish_type_without_duplicate_words():
    fields, _ = compose_canonical_fields(
        {"title": "B12", "collection": "Espresso"},
        {
            "cabinet_door_finish_type": "Stained Stain Stain Finish",
            "collection_color": "Espresso",
        },
        {},
    )

    assert fields["finish_type"] == "Espresso Stain Finish"


def test_compose_canonical_fields_derives_finish_type_from_collection_color_when_finish_text_is_generic():
    fields, _ = compose_canonical_fields(
        {"title": "B12", "collection": "Anna Caramel Harvest"},
        {
            "cabinet_exterior_side_finish": "Matching Door Finish",
        },
        {},
    )

    assert fields["color_finish"] == "Caramel Harvest"
    assert fields["finish_type"] == "Caramel Harvest Stain Finish"


def test_compose_canonical_fields_derives_package_fields_from_alternate_names_and_soft_close_signal():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "package_height_in": "40",
            "package_width_in": "18",
            "package_length_in": "27",
            "package_weight_lb": "91",
            "drawer_notes": "Includes soft close hardware",
        },
        {},
    )

    assert fields["packaged_dimension_height"] == "40"
    assert fields["packaged_dimension_width"] == "18"
    assert fields["packaged_dimension_depth"] == "27"
    assert fields["packaged_gross_weight"] == "91"
    assert fields["soft_close"] == "Yes"


def test_compose_canonical_fields_derives_drawer_slides_from_descriptive_text():
    fields, _ = compose_canonical_fields(
        {"title": "B12"},
        {
            "description": "Includes undermount full extension soft close drawer slides.",
        },
        {},
    )

    assert fields["drawer_slides"] == "Undermount Slides"


def test_compose_canonical_fields_normalizes_depth_from_assembled_depth_values():
    fields, _ = compose_canonical_fields(
        {"title": "B12", "assembly_type": "Assembled"},
        {
            "assembled_depth": '3/4"',
        },
        {},
    )

    assert fields["depth"] == "0.75"


def test_compose_canonical_fields_keeps_only_matching_group_detail_attribute():
    fields, _ = compose_canonical_fields(
        {"title": "W2430"},
        {
            "cabinet_type_sub_category_1": "Wall",
            "cabinet_type_sub_category_2": "Corner Wall Cabinet",
            "base_cabinets": "drawer",
            "wall_cabinets": "corner wall",
            "panels_and_fillers": "fillers",
        },
        {},
    )

    assert fields["cabinet_type"] == "wall cabinets"
    assert fields["wall_cabinets"] == "corner wall cabinet"
    assert "base_cabinets" not in fields
    assert "panels_and_fillers" not in fields


def test_compose_canonical_fields_uses_deeper_category_for_base_detail():
    fields, _ = compose_canonical_fields(
        {"title": "DB12"},
        {
            "cabinet_type_sub_category_1": "Base",
            "cabinet_type_sub_category_2": "Drawer Base Cabinet",
        },
        {},
    )

    assert fields["cabinet_type"] == "base cabinets"
    assert fields["base_cabinets"] == "drawer base cabinet"


def test_compose_canonical_fields_drops_empty_values():
    fields, _ = compose_canonical_fields({"title": "", "brand": None}, {"depth": "12"}, {})
    assert fields == {"depth": "12"}


def test_enrich_catalog_attributes_derives_cabinet_type_from_subcategory():
    enriched = enrich_catalog_attributes({"cabinet_type_sub_category_1": "Base"})
    assert enriched["cabinet_type_sub_category_1"] == "Base Cabinets"
    assert enriched["cabinet_type"] == "base cabinets"


def test_build_channel_payloads_apply_canonical_publish_constraints(catalog_intent_session, monkeypatch):
    session = catalog_intent_session
    monkeypatch.setattr("db.channel_exports._image_fields", lambda session, skus: {})
    monkeypatch.setattr("db.channel_exports.overrides_for_channel", lambda session, channel: {})
    monkeypatch.setattr("db.channel_exports._build_alias_map", lambda session, channel: {})
    monkeypatch.setattr(
        "db.channel_exports._attach_channel_skus",
        lambda session, channel, payloads, connection_id=None: [
            payload.setdefault("channel_sku", payload["sku"]) for payload in payloads
        ],
    )
    monkeypatch.setattr("db.channel_exports._attach_association_fields", lambda *args, **kwargs: None)
    session.add_all(
        [
            MasterProduct(
                sku="ACH-B12",
                name="Assembled Port Bell",
                brand="Port & Bell",
                assembly_type="Assembled",
                category_l1="Kitchen Cabinets",
                row_hash="h1",
                is_active=True,
            ),
            MasterProduct(
                sku="RTA-ACH-B12",
                name="RTA Port Bell",
                brand="Port & Bell",
                assembly_type="RTA",
                category_l1="Kitchen Cabinets",
                row_hash="h2",
                is_active=True,
            ),
            MasterProduct(
                sku="FMM-B12",
                name="Fabuwood",
                brand="Fabuwood",
                assembly_type="Assembled",
                category_l1="Kitchen Cabinets",
                row_hash="h3",
                is_active=True,
            ),
        ]
    )
    session.flush()

    magento_payloads = build_channel_product_payloads(
        session,
        "magento",
        only_assigned=False,
        skip_taxonomy=True,
        publish_constraints={"assembly_type": ["Assembled"]},
    )
    assert [payload["sku"] for payload in magento_payloads] == ["ACH-B12", "FMM-B12"]

    shopify_payloads = build_channel_product_payloads(
        session,
        "shopify",
        only_assigned=False,
        skip_taxonomy=True,
        publish_constraints={"brand": ["Port & Bell"], "assembly_type": ["Assembled", "Ready-to-Assemble"]},
    )
    assert [payload["sku"] for payload in shopify_payloads] == ["ACH-B12", "RTA-ACH-B12"]
    assert shopify_payloads[1]["canonical_fields"]["assembly_type"] == "Ready-to-Assemble"


def test_map_fields_to_channel_uses_aliases_and_passthrough():
    fields = {"title": "Name", "cabinet_door_style": "Shaker"}
    alias_map = {"cabinet_door_style": ["door_style"]}

    mapped = map_fields_to_channel(fields, alias_map)

    assert mapped == {"title": "Name", "door_style": "Shaker"}


def test_map_fields_to_channel_passthroughs_derived_drawer_box():
    fields = {"drawer_box": "Dovetail Solid Wood"}

    mapped = map_fields_to_channel(fields, {})

    assert mapped == {"drawer_box": "Dovetail Solid Wood"}


def test_canonical_shopping_fields_override_stale_attr_values_from_taxonomy_targets():
    payload = {
        "sku": "ASG-W1230",
        "price": 199.0,
        "association_fields": {},
        "canonical_fields": {
            "title": "W1230",
            "category_l1": "Kitchen Cabinets",
            "collection": "Anna Stone Gray",
            "shopping_collection": "anna-stone-gray",
            "shopping_l1": "wall-cabinets",
            "shopping_l2": "wall-cabinets/single-door-glass-wall-cabinet",
        },
        "fields": {
            "title": "W1230",
            "shopping_collection": "anna-stone-gray",
            "shopping_l1": "wall-cabinets",
            "shopping_l2": "wall-cabinets/single-door-glass-wall-cabinet",
        },
        "taxonomy_targets": {
            "plp_path_slugs": [
                "kitchen-cabinets/anna-stone-gray",
                "all-products",
                "kitchen-cabinets/wall-cabinets",
                "kitchen-cabinets/wall-cabinets/basic-wall",
            ]
        },
        "payload_hash": "old-hash",
    }

    _apply_canonical_shopping_field_overrides([payload])

    assert payload["canonical_fields"]["shopping_collection"] == "anna-stone-gray"
    assert payload["canonical_fields"]["shopping_l1"] == "wall-cabinets"
    assert payload["canonical_fields"]["shopping_l2"] == "wall-cabinets/basic-wall"
    assert payload["fields"]["shopping_l2"] == "wall-cabinets/basic-wall"
    assert payload["payload_hash"] != "old-hash"


def test_snapshot_to_magento_product_can_limit_payload_to_selected_attributes():
    payload = snapshot_to_magento_product(
        {
            "sku": "SKU-1",
            "name": "Drawer Base",
            "drawer_box": "Dovetail Solid Wood",
            "color": "White",
            "description": "Test product",
        },
        is_new=False,
        selected_attribute_codes={"drawer_box"},
    )

    assert payload["sku"] == "SKU-1"
    assert payload.get("name") is None
    custom = payload.get("custom_attributes") or []
    assert custom == [{"attribute_code": "drawer_box", "value": "Dovetail Solid Wood"}]


def test_filter_magento_product_payload_removes_non_selected_child_axis_attributes():
    from magento.payload_mapper import _filter_magento_product_payload

    payload = {
        "sku": "SKU-1",
        "custom_attributes": [
            {"attribute_code": "drawer_box", "value": "Dovetail Solid Wood"},
            {"attribute_code": "width", "value": "12"},
        ],
    }

    filtered = _filter_magento_product_payload(payload, {"drawer_box"})

    assert filtered["custom_attributes"] == [{"attribute_code": "drawer_box", "value": "Dovetail Solid Wood"}]


def test_composite_attribute_registry_exposes_derived_magento_fields():
    codes = composite_attribute_codes()

    assert "drawer_box" in codes
    assert "accessories" in codes
    assert "angle" in codes
    assert "box_construction" in codes
    assert "base_cabinets" in codes
    assert "color_finish" in codes
    assert "finish_type" in codes
    assert "overlay" in codes
    assert "hinges" in codes
    assert "drawer_slides" in codes

    drawer_box = derive_composite_attribute_value(
        "drawer_box",
        {
            "drawer_box_construction": "Dovetail",
            "drawer_box_material": "Solid Wood",
            "drawer_box_bottom_material": "Plywood",
        },
    )
    accessories = derive_composite_attribute_value(
        "accessories",
        {"title": "Loose Glass Door"},
    )
    angle = derive_composite_attribute_value(
        "angle",
        {"variation_angle": "upper right"},
    )
    overlay = derive_composite_attribute_value(
        "overlay",
        {"cabinet_door_overlay": "Partial Overlay"},
    )

    assert drawer_box == "Dovetail Solid Wood"
    assert accessories == "glass door"
    assert angle == "Upper Right"
    assert overlay == "Partial Overlay - Framed"


def test_payload_hash_is_stable_and_order_independent():
    h1 = payload_hash({"fields": {"a": 1, "b": 2}, "price": 10.0})
    h2 = payload_hash({"price": 10.0, "fields": {"b": 2, "a": 1}})
    h3 = payload_hash({"fields": {"a": 1, "b": 3}, "price": 10.0})

    assert h1 == h2
    assert h1 != h3


def test_select_price_prefers_channel_specific_type():
    prices = {"base": 100.0, "shopify": 89.0}
    assert select_price(prices, "shopify") == 89.0
    assert select_price(prices, "magento") == 100.0
    assert select_price({}, "shopify") is None


def test_shopify_product_set_input_core_fields_and_seo():
    fields = {
        "title": "Shaker Wall Cabinet",
        "description": "<p>Great cabinet</p>",
        "brand": "Acme",
        "category_l1": "Kitchen Cabinets",
        "meta_title": "Shaker Wall Cabinet | Store",
        "meta_description": "Premium shaker wall cabinet",
        "cabinet_door_style": "Shaker",
        "status": "active",
    }

    product = map_payload_to_product_set_input(
        "SKU-1",
        fields,
        129.99,
        metafield_defs={
            "cabinet_door_style": {
                "namespace": "custom",
                "key": "cabinet_door_style",
                "type": "single_line_text_field",
            }
        },
    )

    assert "id" not in product
    assert product["title"] == "Shaker Wall Cabinet"
    assert product["descriptionHtml"] == "<p>Great cabinet</p>"
    assert product["vendor"] == "Acme"
    assert product["productType"] == "Kitchen Cabinets"
    assert product["status"] == "ACTIVE"
    assert product["seo"] == {
        "title": "Shaker Wall Cabinet | Store",
        "description": "Premium shaker wall cabinet",
    }
    variant = product["variants"][0]
    assert variant["sku"] == "SKU-1"
    assert variant["price"] == "129.99"
    # Non-reserved field lands in metafields under custom namespace.
    metafields = {(m["namespace"], m["key"]): m for m in product["metafields"]}
    assert ("custom", "cabinet_door_style") in metafields


def test_shopify_product_set_input_uses_registry_metafield_definitions():
    fields = {"title": "X", "cabinet_back_material": "Plywood"}
    defs = {
        "cabinet_back_material": {
            "namespace": "specs",
            "key": "back_material",
            "type": "single_line_text_field",
        }
    }

    product = map_payload_to_product_set_input("SKU-2", fields, None, metafield_defs=defs)

    metafield = product["metafields"][0]
    assert metafield["namespace"] == "specs"
    assert metafield["key"] == "back_material"
    assert metafield["value"] == "Plywood"
    assert "price" not in product["variants"][0]


def test_shopify_product_set_input_update_includes_id_and_skips_images():
    fields = {"title": "X", "cabinet_detail_image": "https://cdn/img.jpg", "status": "draft"}

    product = map_payload_to_product_set_input(
        "SKU-3", fields, 10.0, product_id="gid://shopify/Product/123"
    )

    assert product["id"] == "gid://shopify/Product/123"
    assert product["status"] == "DRAFT"
    assert "metafields" not in product


def test_flatten_export_bundle_one_row_per_sku_with_union_columns():
    bundle = {
        "channel_code": "magento",
        "payloads": [
            {
                "sku": "PARENT-1",
                "channel_sku": "TB-PARENT-1",
                "channel_code": "magento",
                "product_role": "parent",
                "fields": {"name": "Parent", "door_style": "Shaker"},
                "override_codes": ["name"],
                "price": None,
                "payload_hash": "abc",
                "relation_fields": {
                    "product_type": "configurable",
                    "variant_list": "TB-CHILD-1,TB-CHILD-2",
                    "configurable_variation_labels": "width=Width",
                },
            },
            {
                "sku": "CHILD-1",
                "channel_sku": "TB-CHILD-1",
                "channel_code": "magento",
                "product_role": "child",
                "fields": {"name": "Child", "height_in": "42"},
                "override_codes": [],
                "price": 129.5,
                "payload_hash": "def",
                "relation_fields": {"variant_of": "TB-PARENT-1", "product_type": "simple"},
            },
        ],
    }

    rows = flatten_export_bundle(bundle)

    assert len(rows) == 2
    parent, child = rows
    assert parent["master_sku"] == "PARENT-1"
    assert parent["channel_sku"] == "TB-PARENT-1"
    assert parent["relation_product_type"] == "configurable"
    assert parent["relation_variant_list"] == "TB-CHILD-1,TB-CHILD-2"
    assert parent["relation_configurable_variation_labels"] == "width=Width"
    assert parent["override_codes"] == "name"
    # Union columns: every row has every field column, blank when absent.
    assert parent["height_in"] == ""
    assert child["height_in"] == "42"
    assert child["door_style"] == ""
    assert child["price"] == 129.5
    assert child["relation_variant_of"] == "TB-PARENT-1"


class _FakeShopifyClient:
    def __init__(self, pages):
        self.pages = pages
        self.calls = 0

    def graphql(self, query, variables=None):
        page = self.pages[self.calls]
        self.calls += 1
        return page


def test_fetch_remote_sku_map_pages_and_keeps_first_product_per_sku():
    pages = [
        {
            "productVariants": {
                "nodes": [
                    {"sku": "SKU-1", "product": {"id": "gid://shopify/Product/1"}},
                    {"sku": "", "product": {"id": "gid://shopify/Product/2"}},
                ],
                "pageInfo": {"hasNextPage": True, "endCursor": "c1"},
            }
        },
        {
            "productVariants": {
                "nodes": [
                    {"sku": "SKU-1", "product": {"id": "gid://shopify/Product/9"}},
                    {"sku": "SKU-2", "product": {"id": "gid://shopify/Product/3"}},
                ],
                "pageInfo": {"hasNextPage": False, "endCursor": None},
            }
        },
    ]
    client = _FakeShopifyClient(pages)

    remote = fetch_remote_sku_map(client)

    assert client.calls == 2
    assert remote == {
        "SKU-1": "gid://shopify/Product/1",
        "SKU-2": "gid://shopify/Product/3",
    }


def test_fetch_remote_sku_map_for_skus_uses_search_query_chunks():
    pages = [
        {
            "productVariants": {
                "nodes": [
                    {"sku": "SKU-A", "product": {"id": "gid://shopify/Product/10"}},
                ],
                "pageInfo": {"hasNextPage": False, "endCursor": None},
            }
        },
        {
            "productVariants": {
                "nodes": [
                    {"sku": "SKU-B", "product": {"id": "gid://shopify/Product/11"}},
                ],
                "pageInfo": {"hasNextPage": False, "endCursor": None},
            }
        },
    ]
    client = _FakeShopifyClient(pages)

    remote = fetch_remote_sku_map_for_skus(client, ["SKU-A", "SKU-B"], chunk_size=1)

    assert client.calls == 2
    assert remote == {
        "SKU-A": "gid://shopify/Product/10",
        "SKU-B": "gid://shopify/Product/11",
    }
