"""Explicit lifecycle and atomic applicability configuration."""
from django.core.exceptions import ValidationError
from django.db import transaction

from apps.catalog.models import ProductCategory
from .models import (
    ComplaintSymptom, ComplaintSymptomProductCategory,
    FaultDiagnosis, FaultDiagnosisProductCategory,
    RootCause, RootCauseProductCategory, RepairAction, RepairActionProductCategory,
)


def _saved(instance, model):
    return (isinstance(instance, model) and instance.pk is not None
        and not instance._state.adding and instance._state.db == "default")


def _set_applicability(*, master, model, mapping_model, master_field,
                       applies_to_all_product_categories, product_categories):
    if not _saved(master, model):
        raise ValueError(f"Supply a saved {model.__name__} from the default database.")
    if type(applies_to_all_product_categories) is not bool:
        raise ValidationError("Global applicability must be a boolean.")
    categories = list(product_categories)
    if any(not _saved(category, ProductCategory) for category in categories):
        raise ValidationError("Supply saved ProductCategory records from the default database.")
    ids = {category.pk for category in categories}
    if applies_to_all_product_categories and ids:
        raise ValidationError("Global applicability cannot include explicit category mappings.")
    with transaction.atomic():
        record = model.objects.select_for_update().get(pk=master.pk)
        existing_categories = list(ProductCategory.objects.filter(pk__in=ids).order_by("pk"))
        if len(existing_categories) != len(ids):
            raise ValidationError("All selected product categories must exist.")
        # Controlled configuration replacement under the master lock. Preserve
        # retained mapping identity/timestamps, remove only entries no longer selected.
        record.category_mappings.exclude(product_category_id__in=ids).delete()
        record.applies_to_all_product_categories = applies_to_all_product_categories
        record.save(update_fields=["applies_to_all_product_categories"])
        present = set(record.category_mappings.values_list("product_category_id", flat=True))
        for category in existing_categories:
            if category.pk not in present:
                mapping_model.objects.create(**{master_field: record}, product_category=category)
        return record


def set_complaint_applicability(*, complaint, applies_to_all_product_categories, product_categories):
    return _set_applicability(master=complaint, model=ComplaintSymptom,
        mapping_model=ComplaintSymptomProductCategory, master_field="complaint_symptom",
        applies_to_all_product_categories=applies_to_all_product_categories, product_categories=product_categories)


def set_fault_diagnosis_applicability(*, diagnosis, applies_to_all_product_categories, product_categories):
    return _set_applicability(master=diagnosis, model=FaultDiagnosis,
        mapping_model=FaultDiagnosisProductCategory, master_field="fault_diagnosis",
        applies_to_all_product_categories=applies_to_all_product_categories, product_categories=product_categories)


def set_root_cause_applicability(*, root_cause, applies_to_all_product_categories, product_categories):
    return _set_applicability(master=root_cause, model=RootCause,
        mapping_model=RootCauseProductCategory, master_field="root_cause",
        applies_to_all_product_categories=applies_to_all_product_categories, product_categories=product_categories)


def set_repair_action_applicability(*, repair_action, applies_to_all_product_categories, product_categories):
    return _set_applicability(master=repair_action, model=RepairAction,
        mapping_model=RepairActionProductCategory, master_field="repair_action",
        applies_to_all_product_categories=applies_to_all_product_categories, product_categories=product_categories)


def _set_active(instance, active):
    with transaction.atomic():
        record = type(instance).objects.select_for_update().get(pk=instance.pk)
        record.is_active = active
        record.save(update_fields=["is_active"])
        return record


def deactivate_service_category(*, service_category):
    return _set_active(service_category, False)


def reactivate_service_category(*, service_category):
    return _set_active(service_category, True)


def deactivate_complaint(*, complaint):
    return _set_active(complaint, False)


def reactivate_complaint(*, complaint):
    return _set_active(complaint, True)


def deactivate_fault_diagnosis(*, diagnosis):
    return _set_active(diagnosis, False)


def reactivate_fault_diagnosis(*, diagnosis):
    return _set_active(diagnosis, True)


def deactivate_root_cause(*, root_cause):
    return _set_active(root_cause, False)


def reactivate_root_cause(*, root_cause):
    return _set_active(root_cause, True)


def deactivate_repair_action(*, repair_action):
    return _set_active(repair_action, False)


def reactivate_repair_action(*, repair_action):
    return _set_active(repair_action, True)
