from decimal import Decimal

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.utils import timezone

from shop_management.models.data.inventory import InventoryBatch, StockBalance, StockMovement
from shop_management.models.data.organization import Warehouse
from shop_management.models.data.product import Product
from shop_management.models.data.sale import Sale, SaleItem
from shop_management.models.data.sale_return import SaleReturn, SaleReturnItem
from shop_management.models.data.shop import Shop
from shop_management.models.data.utils import Currency, Unit
from shop_management.services.inventory import receive_stock
from shop_management.services.sales import post_sale, void_sale
from shop_management.services.sale_returns import post_sale_return, void_sale_return


class MultiWarehouseSaleTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(username="multi-warehouse-sale")
        self.shop = Shop.objects.create(owner=self.user, name="Kabul")
        self.first = Warehouse.objects.create(shop=self.shop, code="A", name="Warehouse A")
        self.second = Warehouse.objects.create(shop=self.shop, code="B", name="Warehouse B")
        self.currency = Currency.objects.create(shop=self.shop, title="AFN")
        self.unit = Unit.objects.create(shop=self.shop, title="Each")
        self.product = Product.objects.create(
            shop=self.shop, name="Medicine", default_unit=self.unit
        )
        self.batch = InventoryBatch.objects.create(product=self.product, batch_number="BATCH-1")
        for warehouse in (self.first, self.second):
            receive_stock(
                warehouse=warehouse, product=self.product, batch=self.batch,
                quantity=Decimal("5"), unit_cost=Decimal("10"),
                currency=self.currency, created_by=self.user,
            )

    def test_post_and_void_move_each_line_in_its_own_warehouse(self):
        sale = self._draft_sale()

        post_sale(sale.pk, user=self.user)
        self.assertEqual(StockBalance.objects.get(warehouse=self.first).quantity, Decimal("4"))
        self.assertEqual(StockBalance.objects.get(warehouse=self.second).quantity, Decimal("3"))
        movement_warehouses = set(
            StockMovement.objects.filter(reference_type="Sale", reference_id=str(sale.pk))
            .values_list("warehouse_id", flat=True)
        )
        self.assertEqual(movement_warehouses, {self.first.pk, self.second.pk})

        void_sale(sale.pk, user=self.user, reason="Test reversal")
        self.assertEqual(StockBalance.objects.get(warehouse=self.first).quantity, Decimal("5"))
        self.assertEqual(StockBalance.objects.get(warehouse=self.second).quantity, Decimal("5"))

    def test_return_and_return_void_use_the_original_line_warehouse(self):
        sale = self._draft_sale()
        post_sale(sale.pk, user=self.user)
        second_item = sale.sale_items.get(warehouse=self.second)
        document = SaleReturn.objects.create(
            sale=sale, shop=self.shop, uses_inventory_engine=True,
            document_status=SaleReturn.DocumentStatus.DRAFT, created_by=self.user,
        )
        SaleReturnItem.objects.create(
            sale_return=document, sale_item=second_item, return_quantity=1,
            refund_amount=0, created_by=self.user,
        )

        post_sale_return(document.pk, user=self.user)
        self.assertEqual(StockBalance.objects.get(warehouse=self.first).quantity, Decimal("4"))
        self.assertEqual(StockBalance.objects.get(warehouse=self.second).quantity, Decimal("4"))

        void_sale_return(document.pk, user=self.user, reason="Test return reversal")
        self.assertEqual(StockBalance.objects.get(warehouse=self.first).quantity, Decimal("4"))
        self.assertEqual(StockBalance.objects.get(warehouse=self.second).quantity, Decimal("3"))

    def _draft_sale(self):
        sale = Sale.objects.create(
            shop=self.shop, warehouse=self.first, currency=self.currency,
            invoice_date=timezone.localdate(), sales_type=Sale.SalesType.CASH,
            uses_inventory_engine=True, document_status=Sale.DocumentStatus.DRAFT,
            created_by=self.user,
        )
        SaleItem.objects.create(
            sale=sale, warehouse=self.first, product=self.product, unit=self.unit,
            quantity=1, sale_price=Decimal("20"), created_by=self.user,
        )
        SaleItem.objects.create(
            sale=sale, warehouse=self.second, product=self.product, unit=self.unit,
            quantity=2, sale_price=Decimal("20"), created_by=self.user,
        )
        return sale
