"""Catalog ancestors (shared), then part category (shared), then part (exclusive)."""
from django.core.exceptions import ValidationError

from apps.access.locks import share_rows
from apps.catalog.models import Brand, ProductCategory, ProductModel, ProductVariant


def persisted_pk(instance, model):
    if not isinstance(instance, model) or instance._state.adding or instance._state.db != "default":
        raise ValidationError(f"Supply a persisted {model.__name__} from the default database.")
    return instance.pk


def lock_catalog(product_models, product_variants):
    """Called within atomic; revalidate snapshots after acquiring ordered locks."""
    try:
        model_ids = {persisted_pk(obj, ProductModel) for obj in product_models}
        variant_ids = {persisted_pk(obj, ProductVariant) for obj in product_variants}
    except TypeError as error:
        raise ValidationError("Supply collections of catalog records.") from error
    variants = dict(ProductVariant.objects.filter(pk__in=variant_ids).values_list("pk", "product_model_id"))
    if len(variants) != len(variant_ids):
        raise ValidationError("A variant no longer exists.")
    if model_ids.intersection(variants.values()):
        raise ValidationError("Choose model-wide or variant-specific compatibility for each model, not both.")
    all_models = model_ids | set(variants.values())
    snapshots = {obj.pk: (obj.brand_id, obj.category_id)
        for obj in ProductModel.objects.filter(pk__in=all_models)}
    if len(snapshots) != len(all_models):
        raise ValidationError("A product model no longer exists.")
    share_rows(Brand, [ids[0] for ids in snapshots.values()])
    share_rows(ProductCategory, [ids[1] for ids in snapshots.values()])
    share_rows(ProductModel, all_models)
    current = {obj.pk: (obj.brand_id, obj.category_id)
        for obj in ProductModel.objects.filter(pk__in=all_models)}
    if current != snapshots:
        raise ValidationError("Catalog dependencies changed; reload and retry.")
    share_rows(ProductVariant, variant_ids)
    if dict(ProductVariant.objects.filter(pk__in=variant_ids).values_list("pk", "product_model_id")) != variants:
        raise ValidationError("Catalog dependencies changed; reload and retry.")
    # Inactive master configuration is allowed, as in service_catalog. Effective
    # compatibility always checks fresh active state in SQL, including ancestors.
    return model_ids, variant_ids
