from sqlalchemy import select

from db.master_taxonomy_merge import (
    merge_taxonomy_nodes,
    register_taxonomy_path_alias,
    resolve_canonical_path_slug,
    suggest_taxonomy_merge_groups,
)
from db.master_taxonomy_sync import upsert_taxonomy_node
from db.models import MasterTaxonomyNode, MasterTaxonomyPathAlias


def test_suggest_plymouth_espresso_merge(catalog_intent_session):
    session = catalog_intent_session
    parent = upsert_taxonomy_node(
        session,
        {"name": "Kitchen Cabinets", "path_slug": "kitchen-cabinets", "node_kind": "hub"},
    )["node"]
    canonical = upsert_taxonomy_node(
        session,
        {
            "name": "Plymouth Espresso",
            "path_slug": "kitchen-cabinets/plymouth-espresso",
            "parent_id": parent["id"],
            "node_kind": "category",
        },
    )["node"]
    alias = upsert_taxonomy_node(
        session,
        {
            "name": "Plymouth Espresso Maple",
            "path_slug": "kitchen-cabinets/plymouth-espresso-maple",
            "parent_id": parent["id"],
            "node_kind": "category",
        },
    )["node"]
    session.flush()

    suggestions = suggest_taxonomy_merge_groups(session, parent_path_slug="kitchen-cabinets")
    assert any(
        row["canonical_node_id"] == canonical["id"] and row["alias_node_id"] == alias["id"]
        for row in suggestions
    )


def test_merge_registers_alias_and_deactivates_node(catalog_intent_session):
    session = catalog_intent_session
    parent = upsert_taxonomy_node(
        session,
        {"name": "Kitchen Cabinets", "path_slug": "kitchen-cabinets", "node_kind": "hub"},
    )["node"]
    canonical = upsert_taxonomy_node(
        session,
        {
            "name": "Plymouth Espresso",
            "path_slug": "kitchen-cabinets/plymouth-espresso",
            "parent_id": parent["id"],
            "node_kind": "category",
        },
    )["node"]
    alias = upsert_taxonomy_node(
        session,
        {
            "name": "Plymouth Espresso Maple",
            "path_slug": "kitchen-cabinets/plymouth-espresso-maple",
            "parent_id": parent["id"],
            "node_kind": "category",
        },
    )["node"]
    session.flush()

    result = merge_taxonomy_nodes(
        session,
        canonical_node_id=canonical["id"],
        alias_node_ids=[alias["id"]],
        dry_run=False,
    )
    session.flush()

    assert result["path_aliases_registered"] == 1
    assert resolve_canonical_path_slug(session, "kitchen-cabinets/plymouth-espresso-maple") == "kitchen-cabinets/plymouth-espresso"
    alias_row = session.get(MasterTaxonomyNode, alias["id"])
    assert alias_row.is_active is False
    alias_map = session.scalar(
        select(MasterTaxonomyPathAlias).where(
            MasterTaxonomyPathAlias.alias_path_slug == "kitchen-cabinets/plymouth-espresso-maple"
        )
    )
    assert alias_map is not None
    assert alias_map.canonical_node_id == canonical["id"]


def test_collection_names_include_merged_alias(catalog_intent_session):
    from db.master_taxonomy_merge import collection_names_for_path_slug

    session = catalog_intent_session
    parent = upsert_taxonomy_node(
        session,
        {"name": "Kitchen Cabinets", "path_slug": "kitchen-cabinets", "node_kind": "hub"},
    )["node"]
    canonical = upsert_taxonomy_node(
        session,
        {
            "name": "Anna Snow White",
            "path_slug": "kitchen-cabinets/anna-snow-white",
            "parent_id": parent["id"],
            "node_kind": "collection",
        },
    )["node"]
    alias = upsert_taxonomy_node(
        session,
        {
            "name": "Snow White",
            "path_slug": "kitchen-cabinets/snow-white",
            "parent_id": parent["id"],
            "node_kind": "collection",
        },
    )["node"]
    session.flush()
    merge_taxonomy_nodes(
        session,
        canonical_node_id=canonical["id"],
        alias_node_ids=[alias["id"]],
        dry_run=False,
    )

    names = collection_names_for_path_slug(session, "kitchen-cabinets/anna-snow-white")
    assert "anna snow white" in names
    assert "snow white" in names


def test_merge_when_sku_already_on_canonical_listing_path(catalog_intent_session):
    """Parents often sit on both short + Gray paths; merge must not UniqueViolation."""
    from db.channel_listing_path import upsert_listing_path
    from db.models import ProductListingPathAssignment

    session = catalog_intent_session
    parent = upsert_taxonomy_node(
        session,
        {"name": "Kitchen Cabinets", "path_slug": "kitchen-cabinets", "node_kind": "hub"},
    )["node"]
    canonical = upsert_taxonomy_node(
        session,
        {
            "name": "Cooper Slate Gray",
            "path_slug": "kitchen-cabinets/cooper-slate-gray",
            "parent_id": parent["id"],
            "node_kind": "collection",
        },
    )["node"]
    alias = upsert_taxonomy_node(
        session,
        {
            "name": "Cooper Slate",
            "path_slug": "kitchen-cabinets/cooper-slate",
            "parent_id": parent["id"],
            "node_kind": "collection",
        },
    )["node"]
    session.flush()

    gray_path = upsert_listing_path(
        session,
        path_slug="kitchen-cabinets/cooper-slate-gray",
        title="Cooper Slate Gray",
        path_kind="collection",
        parent_path_slug="kitchen-cabinets",
    )
    short_path = upsert_listing_path(
        session,
        path_slug="kitchen-cabinets/cooper-slate",
        title="Cooper Slate",
        path_kind="collection",
        parent_path_slug="kitchen-cabinets",
    )
    session.flush()
    gray_path_id = int(gray_path["id"])
    short_path_id = int(short_path["id"])

    # Inactive on Gray still occupies unique (sku, listing_path_id).
    session.add_all(
        [
            ProductListingPathAssignment(
                master_sku="CSG-B-PARENT",
                listing_path_id=gray_path_id,
                assignment_status="inactive",
            ),
            ProductListingPathAssignment(
                master_sku="CSG-B-PARENT",
                listing_path_id=short_path_id,
                assignment_status="active",
            ),
        ]
    )
    session.flush()

    result = merge_taxonomy_nodes(
        session,
        canonical_node_id=canonical["id"],
        alias_node_ids=[alias["id"]],
        dry_run=False,
    )
    session.flush()

    assert result["path_aliases_registered"] == 1
    assert session.get(MasterTaxonomyNode, alias["id"]).is_active is False

    gray_asg = session.scalar(
        select(ProductListingPathAssignment).where(
            ProductListingPathAssignment.listing_path_id == gray_path_id,
            ProductListingPathAssignment.master_sku == "CSG-B-PARENT",
        )
    )
    short_asg = session.scalar(
        select(ProductListingPathAssignment).where(
            ProductListingPathAssignment.listing_path_id == short_path_id,
            ProductListingPathAssignment.master_sku == "CSG-B-PARENT",
        )
    )
    assert gray_asg.assignment_status == "active"
    assert short_asg.assignment_status == "inactive"
