from django.test import SimpleTestCase, TestCase

from api_v1.mdy_plate_order_staging_qiniu_upload import (
    PlateImageWorkItem,
    UploadedImage,
    build_qiniu_key,
    iter_plate_image_work_items,
    process_plate_image_work_item,
)
from api_v1.models import (
    MDYPlateOrderStaging,
    MDYPlateOrderStagingQiniuImageUploadAudit,
)


PLATE_IMAGES_CONTROL_ID = '62d52f4b8d2972284492dd27'


class MDYPlateOrderStagingQiniuUploadWorkItemTest(SimpleTestCase):
    def test_iter_plate_image_work_items_prefers_original_full_path(self):
        staging = MDYPlateOrderStaging(
            id=123,
            mdy_rowid='row-1',
            raw={
                PLATE_IMAGES_CONTROL_ID: [
                    {
                        'original_file_full_path': 'https://p1.mingdaoyun.cn/file-a.jpg',
                        'DownloadUrl': 'https://p1.mingdaoyun.cn/download-a.jpg',
                    }
                ]
            },
        )

        items = list(iter_plate_image_work_items(staging))

        self.assertEqual(len(items), 1)
        self.assertEqual(items[0].attachment_index, 0)
        self.assertEqual(items[0].source_field, 'original_file_full_path')
        self.assertEqual(items[0].original_url, 'https://p1.mingdaoyun.cn/file-a.jpg')
        self.assertEqual(items[0].original_host, 'p1.mingdaoyun.cn')

    def test_build_qiniu_key_is_deterministic_and_keeps_extension(self):
        work_item = PlateImageWorkItem(
            staging=MDYPlateOrderStaging(id=456, mdy_rowid='row-2'),
            attachment_index=3,
            source_field='original_file_full_path',
            original_url='https://p1.mingdaoyun.cn/path/image.png',
            original_host='p1.mingdaoyun.cn',
            raw_attachment={},
        )

        key1 = build_qiniu_key(work_item)
        key2 = build_qiniu_key(work_item)

        self.assertEqual(key1, key2)
        self.assertTrue(key1.startswith('mdy_plate_order_staging/plate_images/staging-456/attachment-3-'))
        self.assertTrue(key1.endswith('.png'))


class MDYPlateOrderStagingQiniuUploadProcessTest(TestCase):
    def test_process_uploads_and_marks_success(self):
        staging = MDYPlateOrderStaging.objects.create(
            mdy_rowid='row-success',
            raw={PLATE_IMAGES_CONTROL_ID: [{'original_file_full_path': 'https://p1.mingdaoyun.cn/a.jpg'}]},
        )
        work_item = next(iter_plate_image_work_items(staging))

        result = process_plate_image_work_item(work_item, upload_func=self._successful_upload)

        self.assertEqual(result.action, 'uploaded')
        audit = MDYPlateOrderStagingQiniuImageUploadAudit.objects.get()
        self.assertEqual(audit.status, MDYPlateOrderStagingQiniuImageUploadAudit.Status.SUCCESS)
        self.assertEqual(audit.attempts, 1)
        self.assertEqual(audit.qiniu_url, 'https://image.yuwen.cloud/a.jpg')
        self.assertEqual(audit.file_size, 12)

    def test_failed_audit_requires_retry_failed_flag(self):
        staging = MDYPlateOrderStaging.objects.create(
            mdy_rowid='row-failed',
            raw={PLATE_IMAGES_CONTROL_ID: [{'original_file_full_path': 'https://p1.mingdaoyun.cn/b.jpg'}]},
        )
        work_item = next(iter_plate_image_work_items(staging))

        first = process_plate_image_work_item(work_item, upload_func=self._failing_upload)
        second = process_plate_image_work_item(work_item, upload_func=self._successful_upload)
        third = process_plate_image_work_item(work_item, retry_failed=True, upload_func=self._successful_upload)

        audit = MDYPlateOrderStagingQiniuImageUploadAudit.objects.get()
        self.assertEqual(first.action, 'failed')
        self.assertEqual(second.action, 'skipped')
        self.assertEqual(second.reason, 'failed_needs_retry_failed')
        self.assertEqual(third.action, 'uploaded')
        self.assertEqual(audit.status, MDYPlateOrderStagingQiniuImageUploadAudit.Status.SUCCESS)
        self.assertEqual(audit.attempts, 2)

    def test_reuses_successful_upload_for_same_original_url(self):
        url = 'https://p1.mingdaoyun.cn/shared.jpg'
        first_staging = MDYPlateOrderStaging.objects.create(
            mdy_rowid='row-first',
            raw={PLATE_IMAGES_CONTROL_ID: [{'original_file_full_path': url}]},
        )
        second_staging = MDYPlateOrderStaging.objects.create(
            mdy_rowid='row-second',
            raw={PLATE_IMAGES_CONTROL_ID: [{'original_file_full_path': url}]},
        )
        first_item = next(iter_plate_image_work_items(first_staging))
        second_item = next(iter_plate_image_work_items(second_staging))

        process_plate_image_work_item(first_item, upload_func=self._successful_upload)
        result = process_plate_image_work_item(second_item, upload_func=self._unexpected_upload)

        self.assertEqual(result.action, 'reused')
        audits = MDYPlateOrderStagingQiniuImageUploadAudit.objects.order_by('id')
        self.assertEqual(audits.count(), 2)
        self.assertEqual(audits[1].qiniu_url, audits[0].qiniu_url)
        self.assertEqual(audits[1].qiniu_key, audits[0].qiniu_key)

    @staticmethod
    def _successful_upload(work_item, **kwargs):
        return UploadedImage(
            qiniu_key='mdy_plate_order_staging/plate_images/a.jpg',
            qiniu_url='https://image.yuwen.cloud/a.jpg',
            file_size=12,
            content_type='image/jpeg',
        )

    @staticmethod
    def _failing_upload(work_item, **kwargs):
        raise RuntimeError('download failed')

    @staticmethod
    def _unexpected_upload(work_item, **kwargs):
        raise AssertionError('upload should have been reused')
