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 stock import models as stock_models

from .fixtures import create_basic_fixtures, create_sales_fixtures


class BusinessServiceEdgeCaseTestCase(TestCase):
    def _disable_auto_stock_tasks(self, merchant):
        basic_models.MerchantSetting.objects.filter(
            merchant=merchant,
            key=basic_models.MerchantSettingKeyEnum.AUTO_CREATE_STOCK_CHANGE_TASKS,
        ).update(val_bool=False)

    def test_sales_order_kind_and_date_validation_errors(self):
        merchant, customer, _, warehouse, _, product, operator = create_sales_fixtures()
        with self.assertRaises(ValueError):
            services.create_sales_order(
                merchant=merchant,
                customer=customer,
                order_date=timezone.now().date(),
                warehouse=warehouse,
                operator=operator,
                items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
                kind='invalid',
            )
        with self.assertRaises(ValueError):
            services.create_sales_order(
                merchant=merchant,
                customer=customer,
                order_date=timezone.now().date(),
                warehouse=warehouse,
                operator=operator,
                items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
                kind=999,
            )
        with self.assertRaises(ValueError):
            services.create_sales_order(
                merchant=merchant,
                customer=customer,
                order_date='not-a-date',
                warehouse=warehouse,
                operator=operator,
                items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
            )
        with self.assertRaises(ValueError):
            services.create_sales_order(
                merchant=merchant,
                customer=customer,
                order_date=object(),
                warehouse=warehouse,
                operator=operator,
                items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
            )

    def test_return_order_source_validation_errors(self):
        merchant, supplier, _, warehouse, product, operator = create_basic_fixtures()
        other_supplier = basic_models.Supplier.objects.create(merchant=merchant, name='其他供应商')
        purchase_order = services.create_purchase_order(
            merchant=merchant,
            supplier=supplier,
            order_date=timezone.now().date(),
            warehouse=warehouse,
            operator=operator,
            items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
        )
        with self.assertRaises(ValueError):
            services.create_purchase_return_order(
                merchant=merchant,
                supplier=supplier,
                return_date=timezone.now().date(),
                warehouse=warehouse,
                operator=operator,
                purchase_order_id=999999,
                items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
            )
        with self.assertRaises(ValueError):
            services.create_purchase_return_order(
                merchant=merchant,
                supplier=other_supplier,
                return_date=timezone.now().date(),
                warehouse=warehouse,
                operator=operator,
                purchase_order=purchase_order,
                items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
            )

        sales_merchant, customer, _, sales_warehouse, _, sales_product, sales_operator = create_sales_fixtures()
        other_customer = basic_models.Customer.objects.create(merchant=sales_merchant, name='其他客户')
        sales_order = services.create_sales_order(
            merchant=sales_merchant,
            customer=customer,
            order_date=timezone.now().date(),
            warehouse=sales_warehouse,
            operator=sales_operator,
            items=[{'product_id': sales_product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
        )
        with self.assertRaises(ValueError):
            services.create_sales_return_order(
                merchant=sales_merchant,
                customer=customer,
                return_date=timezone.now().date(),
                warehouse=sales_warehouse,
                operator=sales_operator,
                sales_order_id=999999,
                items=[{'product_id': sales_product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
            )
        with self.assertRaises(ValueError):
            services.create_sales_return_order(
                merchant=sales_merchant,
                customer=other_customer,
                return_date=timezone.now().date(),
                warehouse=sales_warehouse,
                operator=sales_operator,
                sales_order=sales_order,
                items=[{'product_id': sales_product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
            )

    def test_empty_items_and_update_source_validation_errors(self):
        merchant, supplier, _, warehouse, product, operator = create_basic_fixtures()
        purchase_order = services.create_purchase_order(
            merchant=merchant,
            supplier=supplier,
            order_date=timezone.now().date(),
            warehouse=warehouse,
            operator=operator,
            items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
        )
        with self.assertRaises(ValueError):
            services.update_purchase_order(purchase_order=purchase_order, items=[])
        updated = services.update_purchase_order(
            purchase_order=purchase_order,
            items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
            from_pre_purchase_order_id=123,
        )
        self.assertEqual(updated.from_pre_purchase_order_id, 123)
        sales_merchant, customer, _, sales_warehouse, _, sales_product, sales_operator = create_sales_fixtures()
        with self.assertRaises(ValueError):
            services.create_sales_order(
                merchant=sales_merchant,
                customer=customer,
                order_date=timezone.now().date(),
                warehouse=sales_warehouse,
                operator=sales_operator,
                items=[],
            )
        with self.assertRaises(ValueError):
            services.create_purchase_return_order(
                merchant=merchant,
                supplier=supplier,
                return_date=timezone.now().date(),
                warehouse=warehouse,
                operator=operator,
                items=[],
            )

        return_order = services.create_purchase_return_order(
            merchant=merchant,
            supplier=supplier,
            return_date=timezone.now().date(),
            warehouse=warehouse,
            operator=operator,
            items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
        )
        return_order.purchase_order = None
        return_order.save(update_fields=['purchase_order'])
        with self.assertRaises(ValueError):
            services.update_purchase_return_order(
                purchase_return_order=return_order,
                purchase_order_id=999999,
                items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
            )

        with self.assertRaises(ValueError):
            services.create_sales_return_order(
                merchant=sales_merchant,
                customer=customer,
                return_date=timezone.now().date(),
                warehouse=sales_warehouse,
                operator=sales_operator,
                items=[],
            )

    def test_review_invalid_targets_and_cancelled_reapprove_errors(self):
        merchant, supplier, _, warehouse, product, operator = create_basic_fixtures()
        purchase_order = services.create_purchase_order(
            merchant=merchant,
            supplier=supplier,
            order_date=timezone.now().date(),
            warehouse=warehouse,
            operator=operator,
            items=[{'product_id': product.id, 'quantity': 1, 'num_of_rolls': 1, 'price': '1'}],
        )
        with self.assertRaises(ValueError):
            services.review_purchase_order(purchase_order=purchase_order, target_status=999)
        services.review_purchase_order(
            purchase_order=purchase_order,
            target_status=business_models.PurchaseOrderStatusEnum.CANCELLED,
        )
        with self.assertRaises(ValueError):
            services.review_purchase_order(
                purchase_order=purchase_order,
                target_status=business_models.PurchaseOrderStatusEnum.APPROVED,
            )

        payment = services.create_payment_order(
            merchant=merchant,
            supplier=supplier,
            payment_date=timezone.now().date(),
            amount='1',
            operator=operator,
        )
        services.review_payment_order(payment_order=payment, target_status=business_models.PaymentOrderStatusEnum.CANCELLED)
        with self.assertRaises(ValueError):
            services.review_payment_order(payment_order=payment, target_status=business_models.PaymentOrderStatusEnum.APPROVED)

    def test_resolve_helpers_require_instance_or_id(self):
        with self.assertRaises(ValueError):
            services._resolve_purchase_order_instance(None, None)
        with self.assertRaises(ValueError):
            services._resolve_sales_order_instance(None, None)
        with self.assertRaises(ValueError):
            services._resolve_purchase_return_order_instance(None, None)
        with self.assertRaises(ValueError):
            services._resolve_sales_return_order_instance(None, None)
        with self.assertRaises(ValueError):
            services._resolve_payment_order_instance(None, None)
        with self.assertRaises(ValueError):
            services._resolve_receipt_order_instance(None, None)

    def test_stock_entry_sync_not_found_payloads(self):
        self.assertEqual(
            services.create_purchase_order_stock_entries_sync(
                purchase_order_id=999999,
                warehouse_id=1,
                items=[],
            )['error'],
            'purchase_order_not_found',
        )
        self.assertEqual(
            services.create_sales_order_stock_entries_sync(
                sales_order_id=999999,
                warehouse_id=1,
                items=[],
            )['error'],
            'sales_order_not_found',
        )
        self.assertEqual(
            services.create_purchase_return_order_stock_entries_sync(
                purchase_return_order_id=999999,
                warehouse_id=1,
                items=[],
            )['error'],
            'purchase_return_order_not_found',
        )
        self.assertEqual(
            services.create_sales_return_order_stock_entries_sync(
                sales_return_order_id=999999,
                warehouse_id=1,
                items=[],
            )['error'],
            'sales_return_order_not_found',
        )

    def test_build_stock_flow_items_error_branches(self):
        merchant, supplier, warehouse_strict, _, product, operator = create_basic_fixtures()
        order = services.create_purchase_order(
            merchant=merchant,
            supplier=supplier,
            order_date=timezone.now().date(),
            warehouse=warehouse_strict,
            operator=operator,
            items=[{'product_id': product.id, 'numbers': [1], 'price': '1'}],
        )
        order.items.all().delete()
        with self.assertRaises(ValueError):
            services._build_stock_flow_items_from_order(order)

        order = services.create_purchase_order(
            merchant=merchant,
            supplier=supplier,
            order_date=timezone.now().date(),
            warehouse=warehouse_strict,
            operator=operator,
            items=[{'product_id': product.id, 'numbers': [1], 'price': '1'}],
        )
        item = order.items.first()
        item.quantity_of_rolls = ''
        item.save(update_fields=['quantity_of_rolls'])
        with self.assertRaises(ValueError):
            services._build_stock_flow_items_from_order(order)

    def test_bind_purchase_order_stock_change_record_error_branches(self):
        merchant, supplier, warehouse_strict, warehouse_relaxed, product, operator = create_basic_fixtures()
        self._disable_auto_stock_tasks(merchant)
        order = services.create_purchase_order(
            merchant=merchant,
            supplier=supplier,
            order_date=timezone.now().date(),
            warehouse=warehouse_strict,
            operator=operator,
            items=[{'product_id': product.id, 'numbers': [1], 'price': '1'}],
        )

        bound_record = stock_models.StockChangeRecord.objects.create(
            merchant=merchant,
            type=stock_models.StockChangeTypeEnum.ADD,
            warehouse=warehouse_strict,
            source_type=stock_models.StockChangeSourceEnum.PURCHASE,
            source_id=123,
        )
        with self.assertRaises(ValueError):
            services.bind_purchase_order_stock_change_record(
                purchase_order_id=order.id,
                stock_change_record_id=bound_record.id,
            )

        wrong_warehouse_record = stock_models.StockChangeRecord.objects.create(
            merchant=merchant,
            type=stock_models.StockChangeTypeEnum.ADD,
            warehouse=warehouse_relaxed,
            source_type=stock_models.StockChangeSourceEnum.PURCHASE,
        )
        with self.assertRaises(ValueError):
            services.bind_purchase_order_stock_change_record(
                purchase_order_id=order.id,
                stock_change_record_id=wrong_warehouse_record.id,
            )

        sales_source_record = stock_models.StockChangeRecord.objects.create(
            merchant=merchant,
            type=stock_models.StockChangeTypeEnum.ADD,
            warehouse=warehouse_strict,
            source_type=stock_models.StockChangeSourceEnum.SALES_RETURN,
        )
        with self.assertRaises(ValueError):
            services.bind_purchase_order_stock_change_record(
                purchase_order_id=order.id,
                stock_change_record_id=sales_source_record.id,
            )
