from datetime import timedelta
from decimal import Decimal

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.utils import timezone
from rest_framework.test import APIClient

from shop_management.models.data.inventory import StockBalance, StockMovement
from shop_management.models.data.organization import Warehouse
from shop_management.models.data.product import Product
from shop_management.models.data.purchase import Purchase, PurchaseItem
from shop_management.models.data.purchase_return import PurchaseReturn, PurchaseReturnItem
from shop_management.models.data.shop import Shop
from shop_management.models.data.supplier import Supplier
from shop_management.models.data.utils import Currency, Unit
from shop_management.services.purchases import post_purchase


class PurchaseReturnLifecycleTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(username="returner", password="test", is_staff=True)
        self.user.allowed_tabs = ["purchase_return"]
        self.user.save(update_fields=["allowed_tabs"])
        self.shop = Shop.objects.create(owner=self.user, name="Kabul")
        self.warehouse = Warehouse.objects.create(shop=self.shop, code="MAIN", name="Main")
        self.currency = Currency.objects.create(shop=self.shop, title="AFN")
        self.supplier = Supplier.objects.create(shop=self.shop, name="Vendor")
        self.unit = Unit.objects.create(shop=self.shop, title="Box")
        self.product = Product.objects.create(shop=self.shop, name="Medicine", default_unit=self.unit, requires_batch=True, requires_expiry=True)
        self.purchase = Purchase.objects.create(
            shop=self.shop, supplier=self.supplier, warehouse=self.warehouse,
            currency=self.currency, uses_inventory_engine=True,
        )
        self.item = PurchaseItem.objects.create(
            purchase=self.purchase, product=self.product, unit=self.unit,
            quantity=10, buy_price="20", retail_price="25", batch_number="B-1",
            expire_date=timezone.localdate() + timedelta(days=365),
        )
        post_purchase(self.purchase.id, user=self.user)
        self.client = APIClient()
        self.client.force_authenticate(self.user)

    def create_return(self, quantity=3):
        response = self.client.post("/api/shop/purchase-returns/", {
            "purchase": self.purchase.id, "shop": self.shop.id, "reason": "Damaged",
            "return_items": [{"purchase_item": self.item.id, "return_quantity": quantity, "refund_amount": str(Decimal(quantity) * 20)}],
        }, format="json")
        self.assertEqual(response.status_code, 201, response.data)
        return PurchaseReturn.objects.get(pk=response.data["id"])

    def test_draft_post_and_void_exact_batch(self):
        document = self.create_return()
        self.assertEqual(document.document_status, "DRAFT")
        self.assertEqual(StockBalance.objects.get(warehouse=self.warehouse).quantity, 10)
        posted = self.client.post(f"/api/shop/purchase-returns/{document.id}/post/")
        self.assertEqual(posted.status_code, 200, posted.data)
        document.refresh_from_db()
        self.assertRegex(document.return_number, r"^KAB-PRT-\d{4}-\d{6}$")
        self.assertEqual(StockBalance.objects.get(warehouse=self.warehouse).quantity, 7)
        self.assertEqual(StockMovement.objects.filter(movement_type="PURCHASE_RETURN").count(), 1)
        self.assertEqual(self.item.quantity, 10)

        voided = self.client.post(f"/api/shop/purchase-returns/{document.id}/void/", {"reason": "Supplier rejected return"}, format="json")
        self.assertEqual(voided.status_code, 200, voided.data)
        self.assertEqual(StockBalance.objects.get(warehouse=self.warehouse).quantity, 10)
        self.assertEqual(StockMovement.objects.filter(movement_type="VOID_REVERSAL").count(), 1)

    def test_remaining_returnable_quantity_is_enforced(self):
        first = self.create_return(6)
        self.client.post(f"/api/shop/purchase-returns/{first.id}/post/")
        response = self.client.post("/api/shop/purchase-returns/", {
            "purchase": self.purchase.id, "shop": self.shop.id,
            "return_items": [{"purchase_item": self.item.id, "return_quantity": 5, "refund_amount": "100"}],
        }, format="json")
        self.assertEqual(response.status_code, 400)
        self.assertEqual(StockBalance.objects.get(warehouse=self.warehouse).quantity, 4)

    def test_post_is_atomic_when_batch_stock_is_insufficient(self):
        document = self.create_return(10)
        balance = StockBalance.objects.get(warehouse=self.warehouse)
        balance.quantity = Decimal("5")
        balance.save(update_fields=["quantity"])
        response = self.client.post(f"/api/shop/purchase-returns/{document.id}/post/")
        self.assertEqual(response.status_code, 400)
        document.refresh_from_db()
        self.assertEqual(document.document_status, "DRAFT")
        self.assertEqual(StockMovement.objects.filter(movement_type="PURCHASE_RETURN").count(), 0)
