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 InventoryBatch, StockBalance, StockMovement
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.utils import Currency
from shop_management.models.data.warehouse_transfer import WarehouseTransfer
from shop_management.services.inventory import receive_stock


class WarehouseTransferLifecycleTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(username="transfer", password="test", is_staff=True)
        self.user.allowed_tabs = ["stock_transfer"]
        self.user.save(update_fields=["allowed_tabs"])
        self.shop = Shop.objects.create(owner=self.user, name="Kabul")
        self.source = Warehouse.objects.create(shop=self.shop, code="MAIN", name="Main")
        self.destination = Warehouse.objects.create(shop=self.shop, code="SECOND", name="Second")
        self.currency = Currency.objects.create(shop=self.shop, title="AFN")
        self.product = Product.objects.create(shop=self.shop, name="Medicine", requires_batch=True, requires_expiry=True)
        self.batch = InventoryBatch.objects.create(product=self.product, batch_number="B-100", expiry_date=timezone.localdate() + timedelta(days=90))
        receive_stock(warehouse=self.source, product=self.product, batch=self.batch, quantity=10, unit_cost=25, currency=self.currency)
        StockMovement.objects.all().delete()
        self.client = APIClient()
        self.client.force_authenticate(self.user)

    def create_draft(self, quantity="4"):
        response = self.client.post("/api/shop/warehouse-transfers/", {
            "shop": self.shop.id, "source_warehouse": self.source.id,
            "destination_warehouse": self.destination.id,
            "transfer_date": str(timezone.localdate()), "notes": "Replenishment",
            "items": [{"product": self.product.id, "batch": self.batch.id, "quantity": quantity}],
        }, format="json")
        self.assertEqual(response.status_code, 201, response.data)
        return WarehouseTransfer.objects.get(pk=response.data["id"])

    def test_dispatch_and_receive_preserve_batch_expiry_and_cost(self):
        available = self.client.get(f"/api/shop/warehouse-transfers/available-stock/?warehouse={self.source.id}")
        self.assertEqual(available.status_code, 200, available.data)
        self.assertEqual(Decimal(available.data[0]["quantity"]), Decimal("10"))
        document = self.create_draft()
        self.assertEqual(StockBalance.objects.get(warehouse=self.source, batch=self.batch).quantity, 10)
        response = self.client.post(f"/api/shop/warehouse-transfers/{document.id}/dispatch/")
        self.assertEqual(response.status_code, 200, response.data)
        document.refresh_from_db()
        self.assertEqual(document.status, "DISPATCHED")
        self.assertRegex(document.transfer_number, r"^KAB-WTR-\d{4}-\d{6}$")
        self.assertEqual(StockBalance.objects.get(warehouse=self.source, batch=self.batch).quantity, 6)
        response = self.client.post(f"/api/shop/warehouse-transfers/{document.id}/receive/")
        self.assertEqual(response.status_code, 200, response.data)
        destination = StockBalance.objects.get(warehouse=self.destination, batch=self.batch)
        self.assertEqual(destination.quantity, 4)
        self.assertEqual(destination.average_cost, Decimal("25"))
        self.assertEqual(destination.batch.expiry_date, self.batch.expiry_date)
        self.assertEqual(list(StockMovement.objects.order_by("id").values_list("movement_type", flat=True)), ["TRANSFER_OUT", "TRANSFER_IN"])

    def test_insufficient_stock_rolls_back_entire_dispatch(self):
        document = self.create_draft("11")
        response = self.client.post(f"/api/shop/warehouse-transfers/{document.id}/dispatch/")
        self.assertEqual(response.status_code, 400)
        document.refresh_from_db()
        self.assertEqual(document.status, "DRAFT")
        self.assertEqual(StockBalance.objects.get(warehouse=self.source, batch=self.batch).quantity, 10)
        self.assertEqual(StockMovement.objects.count(), 0)

    def test_void_dispatched_transfer_restores_source(self):
        document = self.create_draft()
        self.client.post(f"/api/shop/warehouse-transfers/{document.id}/dispatch/")
        response = self.client.post(f"/api/shop/warehouse-transfers/{document.id}/void/", {"reason": "Shipment cancelled"}, format="json")
        self.assertEqual(response.status_code, 200, response.data)
        document.refresh_from_db()
        self.assertEqual(document.status, "VOID")
        self.assertEqual(StockBalance.objects.get(warehouse=self.source, batch=self.batch).quantity, 10)
        self.assertFalse(StockBalance.objects.filter(warehouse=self.destination, batch=self.batch).exists())

    def test_received_transfer_cannot_be_voided_after_stock_is_consumed(self):
        document = self.create_draft()
        self.client.post(f"/api/shop/warehouse-transfers/{document.id}/dispatch/")
        self.client.post(f"/api/shop/warehouse-transfers/{document.id}/receive/")
        destination = StockBalance.objects.get(warehouse=self.destination, batch=self.batch)
        destination.quantity = Decimal("1")
        destination.save(update_fields=["quantity"])
        response = self.client.post(f"/api/shop/warehouse-transfers/{document.id}/void/", {"reason": "Wrong warehouse"}, format="json")
        self.assertEqual(response.status_code, 400)
        document.refresh_from_db()
        self.assertEqual(document.status, "RECEIVED")
        self.assertEqual(StockBalance.objects.get(warehouse=self.source, batch=self.batch).quantity, 6)
