from datetime import date
from io import StringIO

from django.apps import apps
from django.contrib.auth import get_user_model
from django.core.management import call_command
from django.db.models import Sum
from django.test import TestCase

from shop_management.models.data.accounting import JournalEntry
from shop_management.models.data.inventory import StockBalance, StockMovement


class SystemDemoSeedTests(TestCase):
    def setUp(self):
        self.owner = get_user_model().objects.create_user(username="demo-test-owner", is_staff=True, is_superuser=True)

    def seed(self, **options):
        call_command("seed_system_demo", count=2, owner=self.owner.pk, date=date(2026, 10, 29), stdout=StringIO(), **options)

    def test_dry_run_preserves_existing_database(self):
        self.seed(dry_run=True)
        self.assertEqual(get_user_model().objects.count(), 1)
        self.assertFalse(apps.get_model("shop_management", "Shop").objects.exists())

    def test_all_business_tables_are_populated_and_rerun_is_idempotent(self):
        self.seed()
        models = list(apps.get_app_config("shop_management").get_models())
        counts = {model: model.objects.count() for model in models}
        for model, count in counts.items():
            with self.subTest(model=model.__name__):
                self.assertGreaterEqual(count, 2)
                for field in model._meta.fields:
                    if field.choices:
                        allowed = {value for value, _ in field.flatchoices}
                        for value in model.objects.values_list(field.name, flat=True):
                            if value not in (None, ""):
                                self.assertIn(value, allowed, f"{model.__name__}.{field.name}")
        self.assertEqual(get_user_model().objects.count(), 3)
        self.assertFalse(get_user_model().objects.get(username="demo_staff_01").has_usable_password())
        for balance in StockBalance.objects.all():
            totals = StockMovement.objects.filter(warehouse=balance.warehouse, product=balance.product, batch=balance.batch).aggregate(incoming=Sum("quantity_in"), outgoing=Sum("quantity_out"))
            self.assertEqual(balance.quantity, totals["incoming"] - totals["outgoing"])
        for entry in JournalEntry.objects.all():
            totals = entry.lines.aggregate(debit=Sum("base_debit"), credit=Sum("base_credit"))
            self.assertEqual(totals["debit"], totals["credit"])
        self.seed()
        self.assertEqual(counts, {model: model.objects.count() for model in models})
