"""Tests for Magento alias fan-out and remote attribute mapper."""

from __future__ import annotations

from datetime import datetime, timezone

from db.channel_exports import _build_alias_map, map_fields_to_channel
from db.channel_remote_attributes import (
    add_remote_attribute_mapping,
    expand_magento_allowed_attribute_codes,
    list_all_master_attribute_values,
    promote_attribute_to_options,
    list_remote_channel_attributes,
)
from db.models import (
    ChannelAttributeAlias,
    MagentoAttributeRegistry,
    MagentoConnection,
    MasterProduct,
    MasterProductAttributeValue,
    MasterStaticFieldMapping,
    ShopifyAttributeRegistry,
    ShopifyConnection,
)
from magento.master_catalog_rows import _payload_to_snapshot_row


def test_payload_to_snapshot_row_uses_aliased_fields_with_fanout():
    row = _payload_to_snapshot_row(
        {
            "sku": "SKU-1",
            "product_role": "standalone",
            "canonical_fields": {
                "title": "Wall Cabinet",
                "collection": "Belmont Drift White",
                "cabinet_door_style": "Shaker",
            },
            "fields": {
                "name": "Wall Cabinet",
                "collection_style": "Belmont Drift White",
                "family": "Belmont Drift White",
                "door_style": "Shaker",
            },
            "price": 99.0,
        }
    )
    assert row["name"] == "Wall Cabinet"
    assert row["collection_style"] == "Belmont Drift White"
    assert row["family"] == "Belmont Drift White"
    assert row["door_style"] == "Shaker"
    assert "collection" not in row
    assert "cabinet_door_style" not in row


def test_payload_to_snapshot_row_falls_back_to_canonical_without_fields():
    row = _payload_to_snapshot_row(
        {
            "sku": "SKU-2",
            "product_role": "standalone",
            "canonical_fields": {
                "title": "Base Cabinet",
                "cabinet_door_style": "Shaker",
            },
            "price": 50.0,
        }
    )
    assert row["name"] == "Base Cabinet"
    assert row["cabinet_door_style"] == "Shaker"


def test_build_alias_map_merges_static_and_dynamic_targets(catalog_intent_session):
    session = catalog_intent_session
    session.add(
        MasterStaticFieldMapping(
            master_attribute_code="collection",
            master_label="Collection",
            field_group="core",
            data_type="text",
            magento_attribute_code="collection_style",
            is_active=True,
        )
    )
    session.add(
        ChannelAttributeAlias(
            channel_code="magento",
            canonical_code="collection",
            channel_attribute_code="collection_style",
            mapping_scope="static",
            action="use_existing",
            is_active=True,
        )
    )
    session.add(
        ChannelAttributeAlias(
            channel_code="magento",
            canonical_code="collection",
            channel_attribute_code="family",
            mapping_scope="dynamic",
            action="use_existing",
            notes="remote_mapper:add_target",
            is_active=True,
        )
    )
    session.commit()

    alias_map = _build_alias_map(session, "magento")
    assert alias_map["collection"] == ["collection_style", "family"]
    mapped = map_fields_to_channel({"collection": "Belmont"}, alias_map)
    assert mapped["collection_style"] == "Belmont"
    assert mapped["family"] == "Belmont"


def test_expand_magento_allowed_includes_alias_targets(catalog_intent_session):
    session = catalog_intent_session
    session.add(
        ChannelAttributeAlias(
            channel_code="magento",
            canonical_code="collection",
            channel_attribute_code="family",
            mapping_scope="dynamic",
            action="use_existing",
            is_active=True,
        )
    )
    session.commit()
    codes = expand_magento_allowed_attribute_codes(session, {"name", "sku"})
    assert "name" in codes
    assert "family" in codes


def test_list_remote_and_add_mapping(catalog_intent_session):
    session = catalog_intent_session
    now = datetime.now(timezone.utc)
    session.add(
        MagentoConnection(
            store_base_url="https://example.test",
            environment="staging",
            consumer_key="k",
            consumer_secret="s",
            access_token="t",
            access_token_secret="ts",
            status="active",
        )
    )
    session.flush()
    conn = session.query(MagentoConnection).one()
    session.add(
        MagentoAttributeRegistry(
            connection_id=conn.id,
            attribute_code="family",
            attribute_id=101,
            frontend_label="Family",
            frontend_input="select",
            backend_type="int",
            is_user_defined=True,
            fetched_at=now,
        )
    )
    session.add(
        MagentoAttributeRegistry(
            connection_id=conn.id,
            attribute_code="collection_style",
            attribute_id=102,
            frontend_label="Collection",
            frontend_input="select",
            backend_type="int",
            is_user_defined=True,
            fetched_at=now,
        )
    )
    session.add(
        MasterProduct(
            sku="SKU-A",
            name="Test",
            collection="Belmont",
            row_hash="x",
            is_active=True,
        )
    )
    session.commit()

    payload = list_remote_channel_attributes(
        session,
        channel_type="magento",
        connection_id=conn.id,
        unmapped_only=True,
    )
    assert payload["unmapped_count"] == 2
    codes = {row["attribute_code"] for row in payload["attributes"]}
    assert codes == {"family", "collection_style"}

    add_remote_attribute_mapping(
        session,
        channel_type="magento",
        channel_attribute_code="family",
        canonical_code="collection",
    )
    add_remote_attribute_mapping(
        session,
        channel_type="magento",
        channel_attribute_code="collection_style",
        canonical_code="collection",
    )
    session.commit()

    payload2 = list_remote_channel_attributes(
        session,
        channel_type="magento",
        connection_id=conn.id,
        unmapped_only=False,
    )
    family = next(row for row in payload2["attributes"] if row["attribute_code"] == "family")
    assert family["mapped"] is True
    assert family["primary_master_code"] == "collection"
    assert family["example"] == "Belmont"
    assert family["can_convert_to_options"] is True

    alias_map = _build_alias_map(session, "magento")
    assert set(alias_map["collection"]) == {"family", "collection_style"}


def test_list_all_master_attribute_values_includes_derived_composite_values(
    catalog_intent_session, monkeypatch
):
    session = catalog_intent_session
    product = MasterProduct(
        sku="SKU-BOX-1",
        name="Drawer Base",
        row_hash="box-1",
        is_active=True,
    )
    session.add(product)
    session.flush()
    session.add_all(
        [
            MasterProductAttributeValue(
                product_id=product.id,
                sku="SKU-BOX-1",
                attribute_code="drawer_box_construction",
                value="Dovetail",
            ),
            MasterProductAttributeValue(
                product_id=product.id,
                sku="SKU-BOX-1",
                attribute_code="drawer_box_material",
                value="Solid Wood",
            ),
            MasterProductAttributeValue(
                product_id=product.id,
                sku="SKU-BOX-1",
                attribute_code="drawer_box_bottom_material",
                value="Plywood",
            ),
        ]
    )
    session.commit()

    def _fail_full_payload_rebuild(*_args, **_kwargs):
        raise AssertionError(
            "list_all_master_attribute_values must not rebuild full channel payloads"
        )

    monkeypatch.setattr(
        "db.channel_exports.build_channel_product_payloads",
        _fail_full_payload_rebuild,
    )

    values = list_all_master_attribute_values(session, "drawer_box")

    assert "Dovetail Solid Wood" in values


def test_list_remote_shopify_attributes_uses_model_backed_connection_meta(catalog_intent_session):
    session = catalog_intent_session
    now = datetime.now(timezone.utc)
    session.add(
        ShopifyConnection(
            shop_code="northpoint-dev",
            shop_domain="northpoint-dev.myshopify.com",
            status="active",
            environment="staging",
        )
    )
    session.flush()
    conn = session.query(ShopifyConnection).one()
    session.add(
        ShopifyAttributeRegistry(
            shop_code=conn.shop_code,
            owner_type="PRODUCT",
            namespace="custom",
            key="collection_style",
            attribute_code="collection_style",
            name="Collection Style",
            data_type="single_line_text_field",
            is_standard=False,
            fetched_at=now,
        )
    )
    session.commit()

    payload = list_remote_channel_attributes(
        session,
        channel_type="shopify",
        connection_id=conn.id,
        unmapped_only=True,
    )

    assert payload["connection"]["display_name"] == "northpoint-dev"
    assert payload["connection"]["shop_domain"] == "northpoint-dev.myshopify.com"
    assert payload["connection"]["environment"] == "staging"
    assert payload["attributes"][0]["attribute_code"] == "collection_style"


def test_promote_attribute_to_options_returns_partial_when_seed_reports_errors(
    catalog_intent_session, monkeypatch
):
    session = catalog_intent_session

    monkeypatch.setattr(
        "db.channel_remote_attributes._resolve_native_connection_id",
        lambda channel, connection_id: connection_id,
    )
    monkeypatch.setattr(
        "db.channel_remote_attributes.list_all_master_attribute_values",
        lambda *args, **kwargs: ["Anna Stone Gray"],
    )
    monkeypatch.setattr(
        "db.channel_remote_attributes.add_remote_attribute_mapping",
        lambda *args, **kwargs: {"status": "ok"},
    )
    monkeypatch.setattr(
        "db.channel_remote_attributes._seed_magento_options",
        lambda *args, **kwargs: {
            "created_options": [],
            "existing_options": [],
            "would_create_options": ["anna-stone-gray"],
            "errors": ["Attribute 'shopping_collection' is 'text' and could not be converted to select (HTTP 400)."],
        },
    )

    result = promote_attribute_to_options(
        session,
        channel_type="magento",
        connection_id=1,
        channel_attribute_code="shopping_collection",
        canonical_code="shopping_collection",
        dry_run=False,
    )

    assert result["status"] == "partial"
    assert result["errors"]
