from sqlalchemy import select

from db.catalog_intent_planner import shopify_collection_targets_for_product
from db.channel_exports import map_fields_to_channel
from db.collection_brand import project_collection_brand
from db.collection_landing_pages import get_collection_landing_page
from db.collection_landing_schema import build_channel_field_payload, magento_attribute_code
from db.master_static_field_mapping import DEFAULT_CHANNEL_FIELD_ALIASES
from db.master_taxonomy_registry import apply_registry_fks_to_product, upsert_brand, upsert_collection_registry
from db.models import CollectionLandingPage, MasterBrand, MasterCollectionRegistry, MasterProduct


def _alias_map(channel: str) -> dict:
    return {code: [remote] for code, remote in DEFAULT_CHANNEL_FIELD_ALIASES[channel].items()}


def test_assigning_collection_brand_projects_to_products_and_landing(catalog_intent_session):
    session = catalog_intent_session
    brand = upsert_brand(session, {"name": "Port & Bell", "code": "port-bell"})
    registry = upsert_collection_registry(
        session,
        {
            "name": "Anna Stone Gray",
            "code": "anna-stone-gray",
            "category_l1": "Kitchen Cabinets",
        },
        allow_invent=True,
    )
    session.add(
        MasterProduct(
            sku="ASG-B15",
            name="B15",
            row_hash="h1",
            is_active=True,
            category_l1="Kitchen Cabinets",
            collection="Anna Stone Gray",
            collection_registry_id=registry["id"],
        )
    )
    session.add(
        CollectionLandingPage(
            path_slug=registry["path_slug"],
            title="Anna Stone Gray",
            category_l1="Kitchen Cabinets",
            collection="Anna Stone Gray",
            collection_registry_id=registry["id"],
            is_active=True,
        )
    )
    session.flush()

    updated = upsert_collection_registry(
        session,
        {
            "id": registry["id"],
            "name": "Anna Stone Gray",
            "code": "anna-stone-gray",
            "brand_id": brand["id"],
        },
        allow_invent=True,
    )
    session.flush()

    product = session.scalar(select(MasterProduct).where(MasterProduct.sku == "ASG-B15"))
    landing = session.scalar(select(CollectionLandingPage).where(CollectionLandingPage.path_slug == registry["path_slug"]))
    assert updated["brand_id"] == brand["id"]
    assert updated["brand_projection"]["products_updated"] == 1
    assert updated["brand_projection"]["landings_updated"] == 1
    assert product.brand_id == brand["id"]
    assert product.brand == "Port & Bell"
    assert product.manufacturer == "Port & Bell"
    assert landing.brand == "Port & Bell"

    magento = map_fields_to_channel({"brand": product.brand, "manufacturer": product.manufacturer}, _alias_map("magento"))
    shopify = map_fields_to_channel({"brand": product.brand}, _alias_map("shopify"))
    assert magento["family"] == "Port & Bell"
    assert magento["manufacturer"] == "Port & Bell"
    assert shopify["vendor"] == "Port & Bell"

    shopify_titles = shopify_collection_targets_for_product(product, [], include_brand=True)
    assert "Port & Bell" in shopify_titles

    landing_payload = build_channel_field_payload(get_collection_landing_page(session, registry["path_slug"]))
    assert landing_payload["brand"] == "Port & Bell"
    assert magento_attribute_code("brand") == "hs_brand"


def test_collection_upsert_without_brand_id_preserves_association(catalog_intent_session):
    session = catalog_intent_session
    brand = upsert_brand(session, {"name": "Port & Bell", "code": "port-bell"})
    created = upsert_collection_registry(
        session,
        {
            "name": "Belmont",
            "code": "belmont",
            "category_l1": "Kitchen Cabinets",
            "brand_id": brand["id"],
        },
        allow_invent=True,
    )
    session.flush()

    updated = upsert_collection_registry(
        session,
        {"id": created["id"], "name": "Belmont", "code": "belmont", "notes": "rename-safe"},
        allow_invent=True,
    )
    session.flush()
    row = session.get(MasterCollectionRegistry, created["id"])
    assert updated["brand_id"] == brand["id"]
    assert row.brand_id == brand["id"]


def test_landing_read_falls_back_to_registry_brand(catalog_intent_session):
    session = catalog_intent_session
    brand = upsert_brand(session, {"name": "CastleCraft", "code": "castlecraft"})
    registry = upsert_collection_registry(
        session,
        {
            "name": "Shaker White",
            "code": "shaker-white",
            "category_l1": "Kitchen Cabinets",
            "brand_id": brand["id"],
        },
        allow_invent=True,
    )
    session.add(
        CollectionLandingPage(
            path_slug=registry["path_slug"],
            title="Shaker White",
            category_l1="Kitchen Cabinets",
            collection="Shaker White",
            collection_registry_id=registry["id"],
            brand=None,
            is_active=True,
        )
    )
    session.flush()

    landing = get_collection_landing_page(session, registry["path_slug"])
    assert landing["brand"] == "CastleCraft"
    assert build_channel_field_payload(landing)["brand"] == "CastleCraft"


def test_product_with_other_brand_id_is_not_overwritten(catalog_intent_session):
    session = catalog_intent_session
    port = upsert_brand(session, {"name": "Port & Bell", "code": "port-bell"})
    castle = upsert_brand(session, {"name": "CastleCraft", "code": "castlecraft"})
    registry = upsert_collection_registry(
        session,
        {
            "name": "Shared Name",
            "code": "shared-name",
            "category_l1": "Kitchen Cabinets",
        },
        allow_invent=True,
    )
    session.add(
        MasterProduct(
            sku="KEEP-1",
            name="Keep",
            row_hash="k1",
            is_active=True,
            collection="Shared Name",
            collection_registry_id=registry["id"],
            brand="CastleCraft",
            brand_id=castle["id"],
        )
    )
    session.flush()

    upsert_collection_registry(
        session,
        {
            "id": registry["id"],
            "name": "Shared Name",
            "code": "shared-name",
            "brand_id": port["id"],
        },
        allow_invent=True,
    )
    session.flush()
    product = session.scalar(select(MasterProduct).where(MasterProduct.sku == "KEEP-1"))
    assert product.brand_id == castle["id"]
    assert product.brand == "CastleCraft"


def test_apply_registry_fks_inherits_collection_brand_when_product_brand_empty(catalog_intent_session):
    session = catalog_intent_session
    brand = upsert_brand(session, {"name": "Port & Bell", "code": "port-bell"})
    registry = upsert_collection_registry(
        session,
        {
            "name": "Anna Caramel Harvest",
            "code": "anna-caramel-harvest",
            "category_l1": "Kitchen Cabinets",
            "brand_id": brand["id"],
        },
        allow_invent=True,
    )
    product = MasterProduct(
        sku="ACH-W24",
        name="W24",
        row_hash="h-ach",
        is_active=True,
        category_l1="Kitchen Cabinets",
        collection="Anna Caramel Harvest",
        collection_registry_id=registry["id"],
    )
    session.add(product)
    session.flush()

    applied = apply_registry_fks_to_product(session, product, create_missing=False)
    session.flush()
    assert applied.get("brand_inherited_from_collection") is True
    assert product.brand == "Port & Bell"
    assert product.brand_id == brand["id"]


def test_project_collection_brand_fills_unlinked_name_matches(catalog_intent_session):
    session = catalog_intent_session
    brand_row = MasterBrand(code="fabuwood", name="Fabuwood", is_active=True)
    registry = MasterCollectionRegistry(
        code="illume",
        name="Illume",
        path_slug="kitchen-cabinets/illume",
        is_active=True,
    )
    session.add_all([brand_row, registry])
    session.flush()
    registry.brand_id = brand_row.id
    session.add(
        MasterProduct(
            sku="ILL-B12",
            name="B12",
            row_hash="ill",
            is_active=True,
            collection="Illume",
        )
    )
    session.flush()

    stats = project_collection_brand(session, registry)
    session.flush()
    product = session.scalar(select(MasterProduct).where(MasterProduct.sku == "ILL-B12"))
    assert stats["products_updated"] == 1
    assert product.brand == "Fabuwood"
    assert product.brand_id == brand_row.id
