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 api_v1.views.business.payment.views import PaymentOrderSerializer
from api_v1.views.business.receipt.views import ReceiptOrderSerializer

from .fixtures import create_basic_fixtures


class PaymentReceiptServiceTestCase(TestCase):
    def setUp(self):
        (
            self.merchant,
            self.supplier,
            self.warehouse_strict,
            self.warehouse_relaxed,
            self.product,
            self.operator,
        ) = create_basic_fixtures()
        self.customer = basic_models.Customer.objects.create(
            merchant=self.merchant,
            name='客户B',
            mobile='13888888888',
            created_by=None,
        )
        self.bank_account = basic_models.BankAccount.objects.create(
            merchant=self.merchant,
            name='测试账户',
            auto_number='BANK-SVC',
        )

    def test_create_payment_order_and_approve(self):
        order = services.create_payment_order(
            merchant=self.merchant,
            supplier=self.supplier,
            payment_date=timezone.now().date(),
            amount='120.50',
            discount_amount='20.50',
            operator=self.operator,
            remarks='付款测试',
            bank_account=self.bank_account,
            markup='服务层附言',
        )
        self.assertEqual(order.status, business_models.PaymentOrderStatusEnum.PENDING)
        self.assertEqual(order.bank_account, self.bank_account)
        self.assertEqual(order.markup, '服务层附言')
        self.assertEqual(order.amount, Decimal('121'))
        self.assertEqual(order.discount_amount, Decimal('21'))
        self.assertEqual(order.settlement_amount, Decimal('142'))
        reviewed = services.review_payment_order(
            payment_order=order,
            target_status=business_models.PaymentOrderStatusEnum.APPROVED,
            reviewed_by=self.operator,
        )
        self.assertEqual(reviewed.status, business_models.PaymentOrderStatusEnum.APPROVED)
        balance = business_models.SupplierBalance.objects.get(
            merchant=self.merchant,
            supplier=self.supplier,
        )
        self.assertEqual(balance.balance, Decimal('-142'))
        record = business_models.BalanceChangeRecord.objects.get(
            merchant=self.merchant,
            source_type=business_models.BalanceChangeSourceEnum.PAYMENT_ORDER,
            source_id=order.id,
        )
        self.assertEqual(record.direction, business_models.BalanceChangeDirectionEnum.DECREASE)
        self.assertEqual(record.delta, Decimal('-142'))
        self.assertEqual(record.balance_after, balance.balance)
        with self.assertRaises(ValueError):
            services.review_payment_order(
                payment_order=order,
                target_status=business_models.PaymentOrderStatusEnum.CANCELLED,
                reviewed_by=self.operator,
            )

    def test_create_receipt_order_and_cancel(self):
        order = services.create_receipt_order(
            merchant=self.merchant,
            customer=self.customer,
            receipt_date=timezone.now().date(),
            amount='80.00',
            discount_amount='5',
            operator=self.operator,
            remarks='收款测试',
            bank_account=self.bank_account,
            markup='收款附言',
        )
        self.assertEqual(order.bank_account, self.bank_account)
        self.assertEqual(order.discount_amount, Decimal('5'))
        self.assertEqual(order.settlement_amount, Decimal('85'))
        cancelled = services.review_receipt_order(
            receipt_order=order,
            target_status=business_models.ReceiptOrderStatusEnum.CANCELLED,
            reviewed_by=self.operator,
        )
        self.assertEqual(cancelled.status, business_models.ReceiptOrderStatusEnum.CANCELLED)
        self.assertFalse(
            business_models.BalanceChangeRecord.objects.filter(
                merchant=self.merchant,
                source_type=business_models.BalanceChangeSourceEnum.RECEIPT_ORDER,
                source_id=order.id,
            ).exists()
        )

    def test_payment_amount_can_be_negative(self):
        order = services.create_payment_order(
            merchant=self.merchant,
            supplier=self.supplier,
            payment_date=timezone.now().date(),
            amount='-25.50',
            operator=self.operator,
        )
        self.assertEqual(order.amount, Decimal('-26'))

    def test_payment_discount_can_exceed_amount(self):
        order = services.create_payment_order(
            merchant=self.merchant,
            supplier=self.supplier,
            payment_date=timezone.now().date(),
            amount='50',
            discount_amount='60',
            operator=self.operator,
        )
        self.assertEqual(order.settlement_amount, Decimal('110'))

    def test_receipt_discount_can_exceed_amount(self):
        order = services.create_receipt_order(
            merchant=self.merchant,
            customer=self.customer,
            receipt_date=timezone.now().date(),
            amount='50',
            discount_amount='60',
            operator=self.operator,
        )
        self.assertEqual(order.settlement_amount, Decimal('110'))

    def test_receipt_amount_can_be_negative(self):
        order = services.create_receipt_order(
            merchant=self.merchant,
            customer=self.customer,
            receipt_date=timezone.now().date(),
            amount='-40',
            operator=self.operator,
        )
        self.assertEqual(order.amount, Decimal('-40'))

    def test_payment_order_rejects_foreign_bank_account(self):
        other_merchant = basic_models.Merchant.objects.create(
            name='无关商户',
            type=basic_models.MerchantTypeEnum.FACTORY,
        )
        foreign_bank = basic_models.BankAccount.objects.create(
            merchant=other_merchant,
            name='其他账户',
            auto_number='BANK-FOREIGN',
        )
        with self.assertRaises(ValueError):
            services.create_payment_order(
                merchant=self.merchant,
                supplier=self.supplier,
                payment_date=timezone.now().date(),
                amount='10',
                operator=self.operator,
                bank_account=foreign_bank,
            )

    def test_receipt_order_rejects_foreign_bank_account(self):
        other_merchant = basic_models.Merchant.objects.create(
            name='第二商户',
            type=basic_models.MerchantTypeEnum.FACTORY,
        )
        foreign_bank = basic_models.BankAccount.objects.create(
            merchant=other_merchant,
            name='第二账户',
            auto_number='BANK-SECOND',
        )
        with self.assertRaises(ValueError):
            services.create_receipt_order(
                merchant=self.merchant,
                customer=self.customer,
                receipt_date=timezone.now().date(),
                amount='10',
                operator=self.operator,
                bank_account=foreign_bank,
            )

    def test_receipt_approval_is_idempotent(self):
        order = services.create_receipt_order(
            merchant=self.merchant,
            customer=self.customer,
            receipt_date=timezone.now().date(),
            amount='10',
            operator=self.operator,
        )
        services.review_receipt_order(
            receipt_order=order,
            target_status=business_models.ReceiptOrderStatusEnum.APPROVED,
            reviewed_by=self.operator,
        )
        reviewed_again = services.review_receipt_order(
            receipt_order=order,
            target_status=business_models.ReceiptOrderStatusEnum.APPROVED,
            reviewed_by=self.operator,
        )
        self.assertEqual(reviewed_again.status, business_models.ReceiptOrderStatusEnum.APPROVED)
        balance = business_models.CustomerBalance.objects.get(
            merchant=self.merchant,
            customer=self.customer,
        )
        self.assertEqual(balance.balance, Decimal('-10'))
        records = business_models.BalanceChangeRecord.objects.filter(
            merchant=self.merchant,
            source_type=business_models.BalanceChangeSourceEnum.RECEIPT_ORDER,
            source_id=order.id,
        )
        self.assertEqual(records.count(), 1)
        self.assertEqual(records.first().balance_after, balance.balance)

    def test_payment_approval_is_idempotent(self):
        order = services.create_payment_order(
            merchant=self.merchant,
            supplier=self.supplier,
            payment_date=timezone.now().date(),
            amount='75.00',
            operator=self.operator,
        )
        services.review_payment_order(
            payment_order=order,
            target_status=business_models.PaymentOrderStatusEnum.APPROVED,
            reviewed_by=self.operator,
        )
        services.review_payment_order(
            payment_order=order,
            target_status=business_models.PaymentOrderStatusEnum.APPROVED,
            reviewed_by=self.operator,
        )
        balance = business_models.SupplierBalance.objects.get(
            merchant=self.merchant,
            supplier=self.supplier,
        )
        self.assertEqual(balance.balance, Decimal('-75.00'))
        records = business_models.BalanceChangeRecord.objects.filter(
            merchant=self.merchant,
            source_type=business_models.BalanceChangeSourceEnum.PAYMENT_ORDER,
            source_id=order.id,
        )
        self.assertEqual(records.count(), 1)
        self.assertEqual(records.first().balance_after, balance.balance)

    def test_payment_order_external_source_fields_default_and_serialized(self):
        order = services.create_payment_order(
            merchant=self.merchant,
            supplier=self.supplier,
            payment_date=timezone.now().date(),
            amount='88.00',
            operator=self.operator,
        )
        self.assertFalse(order.is_external_source)
        self.assertIsNone(order.external_source_id)
        self.assertEqual(order.human_id, f'FK{order.created_at:%Y%m%d}{order.id:06d}')

        order.is_external_source = True
        order.external_source_id = 'XT20260001'
        order.save(update_fields=['is_external_source', 'external_source_id', 'updated_at'])
        order.refresh_from_db()

        serializer = PaymentOrderSerializer(order)
        self.assertTrue(serializer.data['is_external_source'])
        self.assertEqual(serializer.data['external_source_id'], 'XT20260001')
        self.assertEqual(serializer.data['human_id'], order.human_id)

    def test_receipt_order_external_source_fields_default_and_serialized(self):
        order = services.create_receipt_order(
            merchant=self.merchant,
            customer=self.customer,
            receipt_date=timezone.now().date(),
            amount='66.00',
            operator=self.operator,
        )
        self.assertFalse(order.is_external_source)
        self.assertIsNone(order.external_source_id)
        self.assertEqual(order.human_id, f'SK{order.created_at:%Y%m%d}{order.id:06d}')

        order.is_external_source = True
        order.external_source_id = 'SK20225323'
        order.save(update_fields=['is_external_source', 'external_source_id', 'updated_at'])
        order.refresh_from_db()

        serializer = ReceiptOrderSerializer(order)
        self.assertTrue(serializer.data['is_external_source'])
        self.assertEqual(serializer.data['external_source_id'], 'SK20225323')
        self.assertEqual(serializer.data['human_id'], order.human_id)
