from io import StringIO
from unittest.mock import patch

from django.core.management import call_command
from django.test import TestCase

from basic_info import models as basic_models
from printing import models as printing_models


class BackfillExternalProductImagesCommandTest(TestCase):
    def setUp(self):
        self.merchant = basic_models.Merchant.objects.create(
            name='测试商户',
            type=basic_models.MerchantTypeEnum.FACTORY,
        )
        self.other_merchant = basic_models.Merchant.objects.create(
            name='其他商户',
            type=basic_models.MerchantTypeEnum.FACTORY,
        )
        self.category = basic_models.ProductCategory.objects.create(
            merchant=self.merchant,
            name='测试分类',
            product_prefix='TP',
        )
        self.other_category = basic_models.ProductCategory.objects.create(
            merchant=self.other_merchant,
            name='其他分类',
            product_prefix='OP',
        )
        self.customer = basic_models.Customer.objects.create(
            merchant=self.merchant,
            name='测试客户',
        )
        self.other_customer = basic_models.Customer.objects.create(
            merchant=self.other_merchant,
            name='其他客户',
        )

    def _create_product(self, *, merchant, category, name, image=None, description=None):
        return basic_models.Product.objects.create(
            merchant=merchant,
            category=category,
            name=name,
            image=image,
            description=description,
        )

    def _create_printing_order(self, *, merchant, customer, external_order_id=None):
        return printing_models.PrintingOrder.objects.create(
            merchant=merchant,
            customer=customer,
            fabric='测试面料',
            width='1.50',
            external_order_id=external_order_id,
        )

    def _create_printing_job(self, *, order, product, yanse=None):
        raw = {}
        if yanse is not None:
            raw['YanSe'] = yanse
        return printing_models.PrintingJob.objects.create(
            merchant=product.merchant,
            printing_order=order,
            product=product,
            quantity=1,
            unit='段',
            external_raw=raw,
        )

    @patch('api_v1.management.commands.backfill_external_product_images._upload_product_image')
    @patch('api_v1.management.commands.backfill_external_product_images._fetch_external_product_image')
    def test_command_only_processes_external_jobs_with_yanse(
        self,
        mock_fetch_image,
        mock_upload_image,
    ):
        eligible_product = self._create_product(
            merchant=self.merchant,
            category=self.category,
            name='本地产品名#1',
        )
        no_yanse_product = self._create_product(
            merchant=self.merchant,
            category=self.category,
            name='本地产品名#2',
        )
        has_image_product = self._create_product(
            merchant=self.merchant,
            category=self.category,
            name='已有图片产品#3',
            image='product_images/existing.jpg',
        )
        other_product = self._create_product(
            merchant=self.other_merchant,
            category=self.other_category,
            name='其他商户产品#4',
        )

        external_order = self._create_printing_order(
            merchant=self.merchant,
            customer=self.customer,
            external_order_id='KD10001',
        )
        self._create_printing_job(order=external_order, product=eligible_product, yanse='A093#蓝色-L码')
        self._create_printing_job(order=external_order, product=no_yanse_product, yanse='')
        self._create_printing_job(order=external_order, product=has_image_product, yanse='已有图片产品外部名')

        internal_order = self._create_printing_order(
            merchant=self.merchant,
            customer=self.customer,
            external_order_id=None,
        )
        self._create_printing_job(order=internal_order, product=no_yanse_product, yanse='内部订单不应处理')

        other_order = self._create_printing_order(
            merchant=self.other_merchant,
            customer=self.other_customer,
            external_order_id='KD20001',
        )
        self._create_printing_job(order=other_order, product=other_product, yanse='其他商户外部名')

        mock_fetch_image.return_value = {
            'bytes': b'fake-image',
            'content_type': 'image/jpeg',
            'name': 'from-api.jpg',
        }

        stdout = StringIO()
        call_command(
            'backfill_external_product_images',
            '--merchant-id',
            str(self.merchant.id),
            stdout=stdout,
        )

        mock_fetch_image.assert_called_once_with('A093#蓝色-L码')
        self.assertEqual(mock_upload_image.call_count, 1)
        self.assertIn('selected=1', stdout.getvalue())
        self.assertIn('skipped_without_request_name=1', stdout.getvalue())

    @patch('api_v1.management.commands.backfill_external_product_images._upload_product_image')
    @patch('api_v1.management.commands.backfill_external_product_images._fetch_external_product_image')
    def test_limit_applies_after_unique_products_selected(
        self,
        mock_fetch_image,
        mock_upload_image,
    ):
        first_product = self._create_product(
            merchant=self.merchant,
            category=self.category,
            name='本地产品#1',
        )
        second_product = self._create_product(
            merchant=self.merchant,
            category=self.category,
            name='本地产品#2',
        )

        order = self._create_printing_order(
            merchant=self.merchant,
            customer=self.customer,
            external_order_id='KD30001',
        )
        self._create_printing_job(order=order, product=first_product, yanse='A001#红色-S码')
        self._create_printing_job(order=order, product=second_product, yanse='A001#蓝色-L码')

        mock_fetch_image.return_value = {
            'bytes': b'fake-image',
            'content_type': 'image/jpeg',
            'name': 'from-api.jpg',
        }

        stdout = StringIO()
        call_command(
            'backfill_external_product_images',
            '--merchant-id',
            str(self.merchant.id),
            '--limit',
            '1',
            stdout=stdout,
        )

        mock_fetch_image.assert_called_once_with('A001#蓝色-L码')
        self.assertEqual(mock_upload_image.call_count, 1)
        self.assertIn('selected=1', stdout.getvalue())
        self.assertIn('success=1', stdout.getvalue())

    def test_dry_run_lists_only_eligible_products(self):
        eligible = self._create_product(
            merchant=self.merchant,
            category=self.category,
            name='本地产品#dry',
        )
        duplicate = self._create_product(
            merchant=self.merchant,
            category=self.category,
            name='本地产品#duplicate',
        )

        order = self._create_printing_order(
            merchant=self.merchant,
            customer=self.customer,
            external_order_id='KD40001',
        )
        self._create_printing_job(order=order, product=eligible, yanse='A009#紫色-M码')
        self._create_printing_job(order=order, product=duplicate, yanse='')
        self._create_printing_job(order=order, product=eligible, yanse='A009#紫色-M码')

        stdout = StringIO()
        call_command(
            'backfill_external_product_images',
            '--merchant-id',
            str(self.merchant.id),
            '--dry-run',
            stdout=stdout,
        )

        output = stdout.getvalue()
        self.assertIn(eligible.name, output)
        self.assertIn('request_name=A009#紫色-M码', output)
        self.assertNotIn('本地产品#duplicate', output)
        self.assertIn('dry-run 完成', output)

    @patch('api_v1.management.commands.backfill_external_product_images._upload_product_image')
    @patch('api_v1.management.commands.backfill_external_product_images._fetch_external_product_image')
    def test_same_product_is_requested_once_even_if_multiple_jobs_exist(
        self,
        mock_fetch_image,
        mock_upload_image,
    ):
        product = self._create_product(
            merchant=self.merchant,
            category=self.category,
            name='本地产品#same',
        )
        order = self._create_printing_order(
            merchant=self.merchant,
            customer=self.customer,
            external_order_id='KD50001',
        )
        self._create_printing_job(order=order, product=product, yanse='A777#黑色-XL码')
        self._create_printing_job(order=order, product=product, yanse='A777#黑色-XL码')

        mock_fetch_image.return_value = {
            'bytes': b'fake-image',
            'content_type': 'image/jpeg',
            'name': 'from-api.jpg',
        }

        stdout = StringIO()
        call_command(
            'backfill_external_product_images',
            '--merchant-id',
            str(self.merchant.id),
            stdout=stdout,
        )

        mock_fetch_image.assert_called_once_with('A777#黑色-XL码')
        self.assertEqual(mock_upload_image.call_count, 1)
        self.assertIn('skipped_duplicate_product=1', stdout.getvalue())