from decimal import Decimal

from django.test import TestCase
from django.utils import timezone

from basic_info import models as basic_models
from business import models as business_models, services

from .fixtures import create_basic_fixtures, create_sales_fixtures


class OrderBalanceBeforeSnapshotTestCase(TestCase):
    def setUp(self):
        (
            self.supplier_merchant,
            self.supplier,
            _supplier_strict_warehouse,
            self.supplier_warehouse,
            self.supplier_product,
            self.supplier_operator,
        ) = create_basic_fixtures()
        (
            self.customer_merchant,
            self.customer,
            _customer_strict_warehouse,
            self.customer_warehouse,
            _customer_strict_out_warehouse,
            self.customer_product,
            self.customer_operator,
        ) = create_sales_fixtures()

    def _supplier_items(self):
        return [
            {
                'product_id': self.supplier_product.id,
                'quantity': '3',
                'num_of_rolls': 1,
                'price': '12.50',
            }
        ]

    def _customer_items(self):
        return [
            {
                'product_id': self.customer_product.id,
                'quantity': '4',
                'num_of_rolls': 1,
                'price': '8.25',
            }
        ]

    def _create_supplier_orders(self):
        purchase_order = services.create_purchase_order(
            merchant=self.supplier_merchant,
            supplier=self.supplier,
            order_date=timezone.now().date(),
            warehouse=self.supplier_warehouse,
            operator=self.supplier_operator,
            items=self._supplier_items(),
        )
        purchase_return_order = services.create_purchase_return_order(
            merchant=self.supplier_merchant,
            supplier=self.supplier,
            return_date=timezone.now().date(),
            warehouse=self.supplier_warehouse,
            operator=self.supplier_operator,
            items=self._supplier_items(),
            purchase_order=purchase_order,
        )
        return purchase_order, purchase_return_order

    def _create_customer_orders(self):
        sales_order = services.create_sales_order(
            merchant=self.customer_merchant,
            customer=self.customer,
            order_date=timezone.now().date(),
            warehouse=self.customer_warehouse,
            operator=self.customer_operator,
            items=self._customer_items(),
        )
        sales_return_order = services.create_sales_return_order(
            merchant=self.customer_merchant,
            customer=self.customer,
            return_date=timezone.now().date(),
            warehouse=self.customer_warehouse,
            operator=self.customer_operator,
            items=self._customer_items(),
            sales_order=sales_order,
        )
        return sales_order, sales_return_order

    def test_all_four_creation_services_capture_current_balance(self):
        business_models.SupplierBalance.objects.create(
            merchant=self.supplier_merchant,
            supplier=self.supplier,
            balance=Decimal('123.45'),
        )
        business_models.CustomerBalance.objects.create(
            merchant=self.customer_merchant,
            customer=self.customer,
            balance=Decimal('-67.89'),
        )

        purchase_order, purchase_return_order = self._create_supplier_orders()
        sales_order, sales_return_order = self._create_customer_orders()

        self.assertEqual(purchase_order.balance_before_snapshot, Decimal('123.45'))
        self.assertEqual(purchase_return_order.balance_before_snapshot, Decimal('123.45'))
        self.assertEqual(sales_order.balance_before_snapshot, Decimal('-67.89'))
        self.assertEqual(sales_return_order.balance_before_snapshot, Decimal('-67.89'))

    def test_missing_balance_rows_snapshot_zero_instead_of_null(self):
        purchase_order, purchase_return_order = self._create_supplier_orders()
        sales_order, sales_return_order = self._create_customer_orders()

        for order in (
            purchase_order,
            purchase_return_order,
            sales_order,
            sales_return_order,
        ):
            self.assertEqual(order.balance_before_snapshot, Decimal('0'))
            self.assertIsNotNone(order.balance_before_snapshot)

    def test_updates_and_later_balance_changes_do_not_recalculate_snapshot(self):
        supplier_balance = business_models.SupplierBalance.objects.create(
            merchant=self.supplier_merchant,
            supplier=self.supplier,
            balance=Decimal('100.00'),
        )
        customer_balance = business_models.CustomerBalance.objects.create(
            merchant=self.customer_merchant,
            customer=self.customer,
            balance=Decimal('200.00'),
        )
        purchase_order, purchase_return_order = self._create_supplier_orders()
        sales_order, sales_return_order = self._create_customer_orders()

        other_supplier = basic_models.Supplier.objects.create(
            merchant=self.supplier_merchant,
            name='快照测试供应商B',
        )
        other_customer = basic_models.Customer.objects.create(
            merchant=self.customer_merchant,
            name='快照测试客户B',
        )
        business_models.SupplierBalance.objects.create(
            merchant=self.supplier_merchant,
            supplier=other_supplier,
            balance=Decimal('999.00'),
        )
        business_models.CustomerBalance.objects.create(
            merchant=self.customer_merchant,
            customer=other_customer,
            balance=Decimal('888.00'),
        )

        supplier_balance.balance = Decimal('300.00')
        supplier_balance.save(update_fields=['balance', 'updated_at'])
        customer_balance.balance = Decimal('400.00')
        customer_balance.save(update_fields=['balance', 'updated_at'])

        services.update_purchase_order(
            purchase_order=purchase_order,
            supplier=other_supplier,
            items=self._supplier_items(),
        )
        services.update_purchase_return_order(
            purchase_return_order=purchase_return_order,
            supplier=other_supplier,
            items=self._supplier_items(),
        )
        services.update_sales_order(
            sales_order=sales_order,
            customer=other_customer,
            items=self._customer_items(),
        )
        services.update_sales_return_order(
            sales_return_order=sales_return_order,
            customer=other_customer,
            items=self._customer_items(),
        )

        for order in (purchase_order, purchase_return_order):
            order.refresh_from_db()
            self.assertEqual(order.balance_before_snapshot, Decimal('100.00'))
        for order in (sales_order, sales_return_order):
            order.refresh_from_db()
            self.assertEqual(order.balance_before_snapshot, Decimal('200.00'))

    def test_api_serializers_expose_snapshot_as_read_only(self):
        business_models.SupplierBalance.objects.create(
            merchant=self.supplier_merchant,
            supplier=self.supplier,
            balance=Decimal('123.45'),
        )
        business_models.CustomerBalance.objects.create(
            merchant=self.customer_merchant,
            customer=self.customer,
            balance=Decimal('67.89'),
        )
        purchase_order, purchase_return_order = self._create_supplier_orders()
        sales_order, sales_return_order = self._create_customer_orders()

        from api_v1.views.business.purchase.views import PurchaseOrderSerializer
        from api_v1.views.business.purchase_return.views import PurchaseReturnOrderSerializer
        from api_v1.views.business.sales.views import SalesOrderSerializer
        from api_v1.views.business.sales_return.views import SalesReturnOrderSerializer

        cases = (
            (PurchaseOrderSerializer, purchase_order, '123.45'),
            (PurchaseReturnOrderSerializer, purchase_return_order, '123.45'),
            (SalesOrderSerializer, sales_order, '67.89'),
            (SalesReturnOrderSerializer, sales_return_order, '67.89'),
        )
        for serializer_class, order, expected in cases:
            with self.subTest(serializer=serializer_class.__name__):
                self.assertEqual(serializer_class(order).data['balance_before_snapshot'], expected)
                serializer = serializer_class(
                    order,
                    data={'balance_before_snapshot': '9999.99'},
                    partial=True,
                )
                self.assertTrue(serializer.is_valid(), serializer.errors)
                self.assertNotIn('balance_before_snapshot', serializer.validated_data)
