"""Lock Company shared before Customer exclusive, then detail rows."""
from contextlib import contextmanager

from django.core.exceptions import ValidationError
from django.db import connection, transaction

from apps.organization.models import Company


def persisted_pk(instance, model):
    if not isinstance(instance, model) or instance._state.adding or instance._state.db != "default":
        raise ValidationError(f"A persisted {model.__name__} is required.")
    return instance.pk


@contextmanager
def locked_customer(customer_id):
    from .models import Customer

    with transaction.atomic():
        company_id = Customer.objects.values_list("company_id", flat=True).get(pk=customer_id)
        table = connection.ops.quote_name(Company._meta.db_table)
        # FOR SHARE conflicts with Company lifecycle UPDATE locks but permits
        # independent child writes under different customers in the same company.
        with connection.cursor() as cursor:
            cursor.execute(f"SELECT id FROM {table} WHERE id = %s FOR SHARE", [company_id])
            cursor.fetchall()
        yield Customer.objects.select_for_update().get(pk=customer_id)


def require_operational(customer):
    if not customer.is_active or not Company.objects.filter(pk=customer.company_id, is_active=True).exists():
        raise ValidationError("An active customer and company are required.")
