"""Separate PostgreSQL connections; expected waits observed via pg_blocking_pids."""
from django.test import TransactionTestCase

from apps.organization import test_assignment_concurrency as helpers
from . import services
from .models import ProductModel, ProductVariant
from .tests import make_catalog


class CatalogConcurrencyTests(TransactionTestCase):
    run_concurrent = helpers.AssignmentConcurrencyTests.run_concurrent

    def setUp(self):
        self.brand, self.category, self.model, self.variant = make_catalog()

    def create_model(self):
        return ProductModel.objects.create(brand=self.brand, category=self.category, code="NEW", name="New")

    def create_variant(self):
        return ProductVariant.objects.create(product_model=self.model, code="NEW", name="New")

    def test_brand_deactivation_vs_model_creation(self):
        self.run_concurrent(lambda: services.deactivate_brand(brand=self.brand), self.create_model, expected="validation")
        self.assertFalse(ProductModel.objects.active().exists())

    def test_model_creation_then_brand_deactivation(self):
        self.run_concurrent(self.create_model, lambda: services.deactivate_brand(brand=self.brand), expected="success")
        self.assertFalse(ProductModel.objects.active().exists())

    def test_category_deactivation_vs_model_creation(self):
        self.run_concurrent(lambda: services.deactivate_category(category=self.category), self.create_model, expected="validation")
        self.assertFalse(ProductModel.objects.active().exists())

    def test_category_deactivation_vs_model_reactivation(self):
        services.deactivate_product_model(product_model=self.model)
        self.run_concurrent(lambda: services.deactivate_category(category=self.category),
            lambda: services.reactivate_product_model(product_model=self.model), expected="validation")
        self.assertFalse(ProductModel.objects.active().exists())

    def test_brand_deactivation_vs_model_reactivation(self):
        services.deactivate_product_model(product_model=self.model)
        self.run_concurrent(lambda: services.deactivate_brand(brand=self.brand),
            lambda: services.reactivate_product_model(product_model=self.model), expected="validation")
        self.assertFalse(ProductModel.objects.active().exists())

    def test_model_deactivation_vs_variant_creation(self):
        self.run_concurrent(lambda: services.deactivate_product_model(product_model=self.model), self.create_variant, expected="validation")
        self.assertFalse(ProductVariant.objects.active().exists())

    def test_variant_creation_then_model_deactivation(self):
        self.run_concurrent(self.create_variant, lambda: services.deactivate_product_model(product_model=self.model), expected="success")
        self.assertFalse(ProductVariant.objects.active().exists())

    def test_model_deactivation_vs_variant_reactivation(self):
        services.deactivate_variant(variant=self.variant)
        self.run_concurrent(lambda: services.deactivate_product_model(product_model=self.model),
            lambda: services.reactivate_variant(variant=self.variant), expected="validation")
        self.assertFalse(ProductVariant.objects.active().exists())

    def test_category_cascade_vs_variant_creation(self):
        self.run_concurrent(lambda: services.deactivate_category(category=self.category), self.create_variant, expected="validation")
        self.assertFalse(ProductVariant.objects.active().exists())

    def test_distinct_models_share_brand_and_category(self):
        self.run_concurrent(self.create_model,
            lambda: ProductModel.objects.create(brand=self.brand, category=self.category, code="OTHER", name="Other"),
            expected="success", should_block=False)
        self.assertEqual(ProductModel.objects.active().count(), 3)

    def test_overlapping_brand_category_cascades(self):
        self.run_concurrent(lambda: services.deactivate_brand(brand=self.brand),
            lambda: services.deactivate_category(category=self.category), expected="success")
        self.assertFalse(ProductModel.objects.active().exists())
        self.assertFalse(ProductVariant.objects.active().exists())

    def test_model_creation_then_category_deactivation(self):
        self.run_concurrent(self.create_model, lambda: services.deactivate_category(category=self.category), expected="success")
        self.assertFalse(ProductModel.objects.active().exists())

    def test_brand_cascade_vs_variant_creation(self):
        self.run_concurrent(lambda: services.deactivate_brand(brand=self.brand), self.create_variant, expected="validation")
        self.assertFalse(ProductVariant.objects.active().exists())

    def test_reclassification_then_destination_category_deactivation(self):
        from .models import ProductCategory
        destination = ProductCategory.objects.create(code="DEST", name="Destination")
        def reclassify():
            self.model.category = destination
            self.model.save(update_fields=["category"])
        self.run_concurrent(reclassify, lambda: services.deactivate_category(category=destination), expected="success")
        self.model.refresh_from_db()
        self.variant.refresh_from_db()
        self.assertEqual(self.model.category, destination)
        self.assertFalse(self.model.is_active)
        self.assertFalse(self.variant.is_active)

    def test_destination_deactivation_rejects_reclassification(self):
        from .models import ProductCategory
        destination = ProductCategory.objects.create(code="DEST", name="Destination")
        def reclassify():
            self.model.category = destination
            self.model.save(update_fields=["category"])
        self.run_concurrent(lambda: services.deactivate_category(category=destination), reclassify, expected="validation")
        self.model.refresh_from_db()
        self.variant.refresh_from_db()
        self.assertEqual(self.model.category, self.category)
        self.assertTrue(self.model.is_active)
        self.assertTrue(self.variant.is_active)
