from unittest.mock import patch

from django.conf import settings
from django.contrib.auth import get_user_model
from django.test import TestCase
from rest_framework.test import APIClient

from api_v1.tasks import ExternalPrintingOrderSnapshotNotFoundError
from api_v1.views.printing.services import PrintingJobService
from basic_info import models as basic_models
from printing import models as printing_models
from shipment import models as shipment_models
from stateflow import models as stateflow_models
from stateflow import services as stateflow_services


class PrintingOrderExternalSnapshotSyncAPITest(TestCase):
    def setUp(self):
        self.client = APIClient()

        self.merchant = basic_models.Merchant.objects.create(
            name='印染工厂',
            type=basic_models.MerchantTypeEnum.FACTORY,
        )
        self.user = get_user_model().objects.create_user(
            username='factory-sync-user',
            password='pass12345',
        )
        self.employee = basic_models.Employee.objects.create(
            merchant=self.merchant,
            sys_user=self.user,
            name='操作员甲',
        )
        self.client.force_authenticate(user=self.user)

        self.customer = basic_models.Customer.objects.create(
            merchant=self.merchant,
            name='鸿烨服饰',
        )
        self.category = basic_models.ProductCategory.objects.create(
            merchant=self.merchant,
            name='同步产品类',
            product_prefix='TP',
        )
        self.product_keep = basic_models.Product.objects.create(
            merchant=self.merchant,
            category=self.category,
            name='Tj1712#12号色-24码',
            image='product_images/keep.jpg',
        )
        self.product_stale = basic_models.Product.objects.create(
            merchant=self.merchant,
            category=self.category,
            name='Tj1712#12号色-25码',
            image='product_images/stale.jpg',
        )

        state = stateflow_models.State.objects.create(name='待生产')
        self.process = stateflow_models.Process.objects.create(name='默认印染流程')
        self.process.replace_nodes([state])

        settings.PRINTING_DEFAULT_PROCESS_ID = self.process.id
        settings.PRINTING_EXTERNAL_SYNC_USER_ID = self.user.id
        settings.PRINTING_EXTERNAL_SYNC_PRODUCT_CATEGORY_ID = self.category.id

    def _create_printing_order(self, *, external_order_id='KD20432358'):
        return printing_models.PrintingOrder.objects.create(
            merchant=self.merchant,
            customer=self.customer,
            fabric='旧布料',
            width='150cm',
            area='旧地区',
            process=self.process,
            external_order_id=external_order_id,
        )

    def _create_job(self, *, printing_order, product, quantity=10):
        return PrintingJobService.create_printing_job(
            {
                'printing_order': printing_order,
                'product': product,
                'quantity': quantity,
                'unit': '段',
                'size': '2.68',
                'pieces': 10,
                'description': None,
                'original_id': None,
                'external_product_name': None,
                'external_raw': {},
            },
            self.user,
        )

    def _build_snapshot_payload(self, *, external_order_id='KD20432358', records=None):
        return {
            'mode': 'snapshot',
            'external_order_id': external_order_id,
            'status': 'active',
            'snapshot_at': '2026-04-16T10:12:34Z',
            'total_count': len(records or []),
            'records': records or [],
        }

    def _build_record(self, *, record_id=1000001, external_order_id='KD20432358', product_name=None, quantity='10.00'):
        return {
            'BeiZhu': '手感一定要柔软',
            'BeiZhuC': '10件',
            'BianHaoID': external_order_id,
            'BianHaoKD': '1.27',
            'CaoZY': '操作员甲',
            'FidJ': r'\\fw\\2026年-LWQ15\\2026\\H鸿烨\\Tj1712#',
            'HpName': '120克本白四面弹单定',
            'ID': record_id,
            'JiJiaDW': '/段',
            'KdRiQi': '2026-01-26T20:00:52Z',
            'KhID': 'KH00999',
            'MeoA': 'LWQ15',
            'RiQi': '2026-01-26T00:00:00Z',
            'SHDZ': '客户布  烧花',
            'SeHao': '1.51',
            'ShuLiang': quantity,
            'ShuLiangZ': '2.68',
            'SubID': 1,
            'YanSe': product_name or self.product_keep.name,
            'area': '周边',
            'customer': {
                'KhID': 'KH00999',
                'KhName': self.customer.name,
            },
        }

    def test_sync_fails_early_when_order_has_active_sales_items_and_records_audit(self):
        order = self._create_printing_order()
        job = self._create_job(printing_order=order, product=self.product_keep)
        sales_item = shipment_models.SalesItem.objects.create(
            merchant=self.merchant,
            name='销售品A',
            quantity='10.00',
            unit=shipment_models.UnitChoices.PIECE,
            created_by=self.user,
            printing_job_id=job.id,
            customer_id=self.customer.id,
        )

        with patch('api_v1.tasks._fetch_external_printing_order_snapshot') as mock_fetch:
            resp = self.client.post(
                '/api/v2/printing-orders/sync-external-snapshot/',
                {'external_order_id': order.external_order_id},
                format='json',
            )

        self.assertEqual(resp.status_code, 409)
        self.assertIn('销售品', resp.data['detail'])
        self.assertIsNotNone(resp.data['audit_id'])
        mock_fetch.assert_not_called()

        audit = printing_models.PrintingOrderExternalSnapshotSyncAudit.objects.get(id=resp.data['audit_id'])
        self.assertFalse(audit.is_success)
        self.assertIn(str(sales_item.id), audit.failure_reason)
        self.assertEqual(audit.operator_user, self.user)
        self.assertEqual(audit.operator_employee, self.employee)
        self.assertEqual(audit.before_snapshot['printing_order']['id'], order.id)
        self.assertEqual(len(audit.before_snapshot['printing_jobs']), 1)

    def test_sync_fails_when_started_jobs_exist_and_allow_reset_stateflow_is_false(self):
        order = self._create_printing_order()
        job = self._create_job(printing_order=order, product=self.product_keep)
        ok, _msg, _state_log = stateflow_services.advance_to_next_state(job.business_object, self.user)
        self.assertTrue(ok)

        with patch('api_v1.tasks._fetch_external_printing_order_snapshot') as mock_fetch:
            resp = self.client.post(
                '/api/v2/printing-orders/sync-external-snapshot/',
                {'external_order_id': order.external_order_id},
                format='json',
            )

        self.assertEqual(resp.status_code, 409)
        self.assertIn('allow_reset_stateflow=true', resp.data['detail'])
        mock_fetch.assert_not_called()

        audit = printing_models.PrintingOrderExternalSnapshotSyncAudit.objects.get(id=resp.data['audit_id'])
        self.assertFalse(audit.is_success)
        self.assertIn(str(job.id), audit.failure_reason)

    @patch('api_v1.tasks._fetch_external_printing_order_snapshot')
    def test_sync_successfully_updates_order_jobs_and_resets_stateflow_when_allowed(self, mock_fetch_snapshot):
        order = self._create_printing_order()
        keep_job = self._create_job(printing_order=order, product=self.product_keep, quantity=10)
        stale_job = self._create_job(printing_order=order, product=self.product_stale, quantity=20)

        ok, _msg, _state_log = stateflow_services.advance_to_next_state(keep_job.business_object, self.user)
        self.assertTrue(ok)

        mock_fetch_snapshot.return_value = self._build_snapshot_payload(
            records=[
                self._build_record(
                    record_id=1000008,
                    product_name=self.product_keep.name,
                    quantity='25.00',
                )
            ]
        )

        resp = self.client.post(
            '/api/v2/printing-orders/sync-external-snapshot/',
            {
                'external_order_id': order.external_order_id,
                'allow_reset_stateflow': True,
            },
            format='json',
        )

        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.data['orders_updated'], 1)
        self.assertEqual(resp.data['jobs_updated'], 1)
        self.assertEqual(resp.data['jobs_deleted'], 1)
        self.assertEqual(resp.data['reset_stateflow_job_count'], 1)

        order.refresh_from_db()
        keep_job.refresh_from_db()
        self.assertEqual(order.fabric, '120克本白四面弹单定')
        self.assertEqual(order.area, '周边')
        self.assertEqual(keep_job.quantity, 25)
        self.assertEqual(keep_job.original_id, 1000008)
        self.assertEqual(keep_job.sub_id, 1)
        self.assertFalse(printing_models.PrintingJob.objects.filter(id=stale_job.id).exists())
        self.assertEqual(keep_job.business_object.state_logs.filter(is_cancelled=False).count(), 0)

        audit = printing_models.PrintingOrderExternalSnapshotSyncAudit.objects.get(id=resp.data['audit_id'])
        self.assertTrue(audit.is_success)
        self.assertEqual(audit.printing_order_id, order.id)
        self.assertTrue(audit.allow_reset_stateflow)
        self.assertEqual(audit.before_snapshot['printing_order']['fabric'], '旧布料')
        self.assertEqual(len(audit.before_snapshot['printing_jobs']), 2)
        self.assertIn('sub_id', audit.before_snapshot['printing_jobs'][0])

    @patch('api_v1.tasks._fetch_external_product_image')
    @patch('api_v1.tasks._fetch_external_printing_order_snapshot')
    def test_sync_allows_missing_image_for_white_fabric_product(self, mock_fetch_snapshot, mock_fetch_image):
        product_name = '外部白布-快照款'
        mock_fetch_snapshot.return_value = self._build_snapshot_payload(
            records=[
                self._build_record(
                    record_id=1000018,
                    product_name=product_name,
                    quantity='12.00',
                )
            ]
        )
        mock_fetch_image.side_effect = RuntimeError(f'外部图片不存在: {product_name}')

        resp = self.client.post(
            '/api/v2/printing-orders/sync-external-snapshot/',
            {'external_order_id': 'KD20432358'},
            format='json',
        )

        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.data['jobs_created'], 1)

        product = basic_models.Product.objects.get(merchant=self.merchant, name=product_name)
        self.assertFalse(product.image)
        job = printing_models.PrintingJob.objects.get(original_id=1000018)
        self.assertEqual(job.product, product)

        audit = printing_models.PrintingOrderExternalSnapshotSyncAudit.objects.get(id=resp.data['audit_id'])
        self.assertTrue(audit.is_success)
        self.assertEqual(audit.failure_reason, '')

    @patch('api_v1.tasks._fetch_external_printing_order_snapshot')
    def test_sync_records_audit_when_external_snapshot_not_found(self, mock_fetch_snapshot):
        mock_fetch_snapshot.side_effect = ExternalPrintingOrderSnapshotNotFoundError('未找到该 external_order_id 对应的订单')

        resp = self.client.post(
            '/api/v2/printing-orders/sync-external-snapshot/',
            {'external_order_id': 'KD-NOT-FOUND'},
            format='json',
        )

        self.assertEqual(resp.status_code, 404)
        self.assertIn('未找到', resp.data['detail'])

        audit = printing_models.PrintingOrderExternalSnapshotSyncAudit.objects.get(id=resp.data['audit_id'])
        self.assertFalse(audit.is_success)
        self.assertEqual(audit.external_order_id, 'KD-NOT-FOUND')
        self.assertEqual(audit.before_snapshot['exists'], False)
