"""Locks compatible with frozen organization, Customer, catalog and Device writers."""
from contextlib import contextmanager

from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.db import connection, transaction

from apps.customers.models import Customer
from apps.devices.locking import persisted_pk, locked_catalog, require_catalog_active
from apps.devices.identification import validate_configuration
from apps.devices.models import Device, CustomerDeviceRelationship
from apps.organization.models import Company, ServiceCenter, Region


def share(model, pk):
    table = connection.ops.quote_name(model._meta.db_table)
    with connection.cursor() as cursor:
        cursor.execute(f"SELECT id FROM {table} WHERE id = %s FOR SHARE", [pk])
        if cursor.fetchone() is None:
            raise ValidationError(f"A persisted {model.__name__} is required.")
    return model.objects.get(pk=pk)


@contextmanager
def intake_context(*, company, service_center, customer, device, created_by):
    ids = [persisted_pk(obj, model) for obj, model in (
        (company, Company), (service_center, ServiceCenter), (customer, Customer),
        (device, Device), (created_by, get_user_model()),
    )]
    company_id, center_id, customer_id, device_id, user_id = ids
    with transaction.atomic():
        actor = share(get_user_model(), user_id)
        company = share(Company, company_id)
        center = ServiceCenter.objects.select_for_update().get(pk=center_id)
        if center.company_id != company.pk:
            raise ValidationError("ServiceCenter belongs to another Company.")
        # Company SHARE prevents supported Region/Center movement and lifecycle.
        if not (actor.is_active and company.is_active and center.is_active and
                Region.objects.filter(pk=center.region_id, company=company, is_active=True).exists()):
            raise ValidationError("Active creator, Company and ServiceCenter hierarchy are required.")
        customer = share(Customer, customer_id)
        if customer.company_id != company.pk or not customer.is_active:
            raise ValidationError("An active Customer in the intake Company is required.")
        snapshot = Device.objects.get(pk=device_id)
        with locked_catalog(snapshot.product_model_id, snapshot.product_variant_id) as (model, variant):
            device = Device.objects.select_for_update().get(pk=device_id)
            if (device.product_model_id, device.product_variant_id) != (snapshot.product_model_id, snapshot.product_variant_id):
                raise ValidationError("Device catalog identity changed; reload.")
            require_catalog_active(model, variant)
            if not device.is_active:
                raise ValidationError("An active Device is required.")
            validate_configuration(model, {row.identifier_type: row.value for row in device.identifiers.filter(is_active=True)})
            if CustomerDeviceRelationship.objects.filter(device=device, relationship_type="OWNER").exclude(customer__company=company).exists():
                raise ValidationError("Device ownership history belongs to another Company.")
            yield company, center, customer, device, actor
