from datetime import timedelta

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.utils import timezone

from shop_management.models.data.customer import Customer
from shop_management.models.data.inventory import InventoryBatch, StockBalance
from shop_management.models.data.notification import OperationalAlert
from shop_management.models.data.organization import Warehouse
from shop_management.models.data.product import Product
from shop_management.models.data.shop import Shop
from shop_management.models.data.supplier import Supplier
from shop_management.models.data.utils import Currency
from shop_management.services.notifications import sync_operational_alerts


class OperationalAlertTests(TestCase):
    def setUp(self):
        User = get_user_model()
        self.user = User.objects.create_user(username="manager", password="x", is_staff=True)
        self.shop = Shop.objects.create(owner=self.user, name="Kabul")
        self.currency = Currency.objects.create(shop=self.shop, title="AFN")
        self.warehouse = Warehouse.objects.create(shop=self.shop, code="MAIN", name="Main")
        self.low_product = Product.objects.create(shop=self.shop, name="Low item", minimum_stock_level=5)
        self.near_product = Product.objects.create(shop=self.shop, name="Near item", requires_batch=True, requires_expiry=True)
        self.expired_product = Product.objects.create(shop=self.shop, name="Expired item", requires_batch=True, requires_expiry=True)
        self.low_batch = InventoryBatch.objects.create(product=self.low_product, batch_number="LOW")
        self.near_batch = InventoryBatch.objects.create(product=self.near_product, batch_number="NEAR", expiry_date=timezone.localdate() + timedelta(days=10))
        self.expired_batch = InventoryBatch.objects.create(product=self.expired_product, batch_number="OLD", expiry_date=timezone.localdate() - timedelta(days=1))
        for product, batch, quantity in [(self.low_product, self.low_batch, 2), (self.near_product, self.near_batch, 3), (self.expired_product, self.expired_batch, 4)]:
            StockBalance.objects.create(warehouse=self.warehouse, product=product, batch=batch, quantity=quantity, average_cost=1, cost_currency=self.currency)
        Customer.objects.create(shop=self.shop, name="Customer A", opening_balance=100, opening_balance_currency=self.currency)
        Supplier.objects.create(shop=self.shop, name="Supplier A", opening_balance=200, opening_balance_currency=self.currency)

    def test_generates_all_five_alert_types_without_duplicates(self):
        sync_operational_alerts(self.user)
        self.assertEqual(
            set(OperationalAlert.objects.filter(is_active=True).values_list("alert_type", flat=True)),
            {"LOW_STOCK", "NEAR_EXPIRY", "EXPIRED_STOCK", "CUSTOMER_DUE", "SUPPLIER_DUE"},
        )
        count = OperationalAlert.objects.count()
        sync_operational_alerts(self.user)
        self.assertEqual(OperationalAlert.objects.count(), count)

    def test_read_state_is_preserved_while_problem_remains(self):
        sync_operational_alerts(self.user)
        alert = OperationalAlert.objects.filter(alert_type="LOW_STOCK").get()
        alert.is_read = True
        alert.save(update_fields=["is_read"])
        sync_operational_alerts(self.user)
        alert.refresh_from_db()
        self.assertTrue(alert.is_read)
        self.assertTrue(alert.is_active)

    def test_alert_resolves_when_condition_is_removed(self):
        sync_operational_alerts(self.user)
        balance = StockBalance.objects.get(product=self.low_product)
        balance.quantity = 10
        balance.save()
        sync_operational_alerts(self.user)
        alert = OperationalAlert.objects.get(alert_type="LOW_STOCK")
        self.assertFalse(alert.is_active)
        self.assertIsNotNone(alert.resolved_at)
