"""Collection brand association and downstream projection.

``master_collection_registry.brand_id`` is the source of truth for which brand
a collection belongs to. Assigning it projects onto:

* ``collection_landing_page.brand`` — Magento ``hs_brand`` and Shopify collection metafield
* member ``master_product.brand`` / ``brand_id`` — Magento ``family`` and Shopify ``vendor``
* Shopify brand collections (planner reads ``product.brand`` when include_brand is on)

Product-level brand_id that already points at a different brand is left alone.
"""

from __future__ import annotations

from typing import Any, Dict, List, Optional

from sqlalchemy import and_, or_, select
from sqlalchemy.orm import Session

from db.models import CollectionLandingPage, MasterBrand, MasterCollectionRegistry, MasterProduct


def brand_for_registry(session: Session, registry: MasterCollectionRegistry) -> Optional[MasterBrand]:
    if registry is None or registry.brand_id is None:
        return None
    return session.get(MasterBrand, int(registry.brand_id))


def brand_name_for_registry_id(session: Session, registry_id: Optional[int]) -> Optional[str]:
    if registry_id is None:
        return None
    registry = session.get(MasterCollectionRegistry, int(registry_id))
    brand = brand_for_registry(session, registry) if registry is not None else None
    name = str(brand.name or "").strip() if brand is not None else ""
    return name or None


def inherit_product_brand_from_collection(session: Session, product: MasterProduct) -> bool:
    """Fill empty product brand fields from the linked collection registry."""
    if product is None or product.collection_registry_id is None:
        return False
    registry = session.get(MasterCollectionRegistry, int(product.collection_registry_id))
    brand = brand_for_registry(session, registry) if registry is not None else None
    if brand is None:
        return False
    return _apply_brand_to_product(product, brand, overwrite=False)


def project_collection_brand(
    session: Session,
    registry: MasterCollectionRegistry,
    *,
    previous_brand_id: Optional[int] = None,
) -> Dict[str, Any]:
    """Write collection brand onto landings and member products."""
    brand = brand_for_registry(session, registry)
    brand_name = str(brand.name or "").strip() if brand is not None else ""
    landings_updated = _project_landing_brand(session, registry, brand_name or None)
    products_updated = 0
    for product in _member_products(session, registry):
        if not _should_apply_collection_brand(product, previous_brand_id=previous_brand_id):
            continue
        if brand is None:
            continue
        if _apply_brand_to_product(product, brand, overwrite=True):
            products_updated += 1
    session.flush()
    return {
        "brand_id": int(brand.id) if brand is not None else None,
        "brand": brand_name or None,
        "landings_updated": landings_updated,
        "products_updated": products_updated,
    }


def _project_landing_brand(
    session: Session,
    registry: MasterCollectionRegistry,
    brand_name: Optional[str],
) -> int:
    if not brand_name:
        return 0
    updated = 0
    for landing in _landings_for_registry(session, registry):
        if str(landing.brand or "").strip() == brand_name:
            continue
        landing.brand = brand_name
        if landing.collection_registry_id is None:
            landing.collection_registry_id = registry.id
        updated += 1
    return updated


def _landings_for_registry(session: Session, registry: MasterCollectionRegistry) -> List[CollectionLandingPage]:
    filters = [CollectionLandingPage.collection_registry_id == registry.id]
    if registry.path_slug:
        filters.append(CollectionLandingPage.path_slug == registry.path_slug)
    return list(session.scalars(select(CollectionLandingPage).where(or_(*filters))).all())


def _member_products(session: Session, registry: MasterCollectionRegistry) -> List[MasterProduct]:
    filters = [MasterProduct.collection_registry_id == registry.id]
    name = str(registry.name or "").strip()
    if name:
        filters.append(
            and_(
                MasterProduct.collection_registry_id.is_(None),
                MasterProduct.collection == name,
            )
        )
    return list(
        session.scalars(
            select(MasterProduct).where(MasterProduct.is_active.is_(True)).where(or_(*filters))
        ).all()
    )


def _should_apply_collection_brand(product: MasterProduct, *, previous_brand_id: Optional[int]) -> bool:
    if product.brand_id is None:
        return True
    if previous_brand_id is not None and product.brand_id == previous_brand_id:
        return True
    return not str(product.brand or "").strip()


def _apply_brand_to_product(product: MasterProduct, brand: MasterBrand, *, overwrite: bool) -> bool:
    brand_name = str(brand.name or "").strip()
    if not brand_name:
        return False
    changed = False
    if product.brand_id != int(brand.id) and (overwrite or product.brand_id is None):
        product.brand_id = int(brand.id)
        changed = True
    if str(product.brand or "").strip() != brand_name and (overwrite or not str(product.brand or "").strip()):
        product.brand = brand_name
        changed = True
    if not str(product.manufacturer or "").strip():
        product.manufacturer = brand_name
        changed = True
    return changed
