package com.njcn.gather.aireport.testdevice.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.aireport.testdevice.component.TestDeviceReportStorageService;
import com.njcn.gather.aireport.testdevice.mapper.TestDeviceMapper;
import com.njcn.gather.aireport.testdevice.pojo.param.TestDeviceParam;
import com.njcn.gather.aireport.testdevice.pojo.po.TestDevicePO;
import com.njcn.gather.aireport.testdevice.pojo.vo.TestDeviceTypeNameVO;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.util.ReflectionTestUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Collections;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

class TestDeviceServiceImplTest {

    private final TestDeviceMapper testDeviceMapper = Mockito.mock(TestDeviceMapper.class);
    private final TestDeviceReportStorageService reportStorageService = Mockito.mock(TestDeviceReportStorageService.class);

    @Test
    void addTestDeviceShouldRejectExistingDeletedNumber() {
        TestableService service = buildService();
        service.countResult = 1;
        Mockito.when(testDeviceMapper.countNormalDeviceType("type-1")).thenReturn(1);

        TestDeviceParam.AddParam param = new TestDeviceParam.AddParam();
        param.setType("type-1");
        param.setNo("TD-001");
        param.setValidityPeriod("2026-06-25");

        BusinessException exception = Assertions.assertThrows(BusinessException.class,
                () -> service.addTestDevice(param, new MockMultipartFile("reportFile", "report.pdf",
                        "application/pdf", "%PDF-1.4".getBytes())));

        Assertions.assertEquals("设备编号已存在", exception.getMessage());
        Mockito.verifyNoInteractions(reportStorageService);
    }

    @Test
    void updateTestDeviceShouldDeleteNewReportWhenUpdateReturnsFalse() {
        TestableService service = buildService();
        service.countResult = 0;
        service.updateResult = false;
        service.requiredDevice = buildDevice("device-1", "old/report.pdf");
        Mockito.when(testDeviceMapper.countNormalDeviceType("type-1")).thenReturn(1);
        Mockito.when(reportStorageService.save(Mockito.any())).thenReturn("new/report.pdf");

        TestDeviceParam.UpdateParam param = new TestDeviceParam.UpdateParam();
        param.setId("device-1");
        param.setType("type-1");
        param.setNo("TD-001");
        param.setValidityPeriod("2026-06-25");

        boolean result = service.updateTestDevice(param,
                new MockMultipartFile("reportFile", "report.pdf", "application/pdf", "%PDF-1.4".getBytes()));

        Assertions.assertFalse(result);
        Mockito.verify(reportStorageService).deleteQuietly("new/report.pdf");
        Mockito.verify(reportStorageService, Mockito.never()).deleteQuietly("old/report.pdf");
    }

    @Test
    void importTestDevicesShouldRejectOversizedPdfInZip() throws Exception {
        TestableService service = buildService();
        Mockito.when(testDeviceMapper.selectNormalDeviceTypes()).thenReturn(Collections.singletonList(buildType("type-1", "示波器")));

        BusinessException exception = Assertions.assertThrows(BusinessException.class,
                () -> service.importTestDevices(buildExcelFile(), buildZipFile(10 * 1024 * 1024 + 1)));

        Assertions.assertEquals("校准报告不能超过10MB", exception.getMessage());
    }

    private TestableService buildService() {
        TestableService service = new TestableService(reportStorageService);
        ReflectionTestUtils.setField(service, "baseMapper", testDeviceMapper);
        return service;
    }

    private TestDevicePO buildDevice(String id, String reportPath) {
        TestDevicePO device = new TestDevicePO();
        device.setId(id);
        device.setType("type-1");
        device.setNo("TD-001");
        device.setValidityPeriod(LocalDate.of(2026, 6, 25));
        device.setValidityReport(reportPath);
        return device;
    }

    private TestDeviceTypeNameVO buildType(String id, String name) {
        TestDeviceTypeNameVO type = new TestDeviceTypeNameVO();
        type.setId(id);
        type.setName(name);
        return type;
    }

    private MockMultipartFile buildExcelFile() throws IOException {
        try (XSSFWorkbook workbook = new XSSFWorkbook();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            XSSFSheet sheet = workbook.createSheet("导入");
            sheet.createRow(0).createCell(0).setCellValue("设备型号");
            sheet.getRow(0).createCell(1).setCellValue("设备编号");
            sheet.getRow(0).createCell(2).setCellValue("校准有效期");
            sheet.getRow(0).createCell(3).setCellValue("设备状态");
            sheet.getRow(0).createCell(4).setCellValue("校准报告文件名");
            sheet.createRow(1).createCell(0).setCellValue("示波器");
            sheet.getRow(1).createCell(1).setCellValue("TD-001");
            sheet.getRow(1).createCell(2).setCellValue("2026-06-25");
            sheet.getRow(1).createCell(3).setCellValue("在运");
            sheet.getRow(1).createCell(4).setCellValue("report.pdf");
            workbook.write(outputStream);
            return new MockMultipartFile("file", "test-device.xlsx",
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", outputStream.toByteArray());
        }
    }

    private MockMultipartFile buildZipFile(int pdfSize) throws IOException {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
             ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
            zipOutputStream.putNextEntry(new ZipEntry("report.pdf"));
            zipOutputStream.write(new byte[pdfSize]);
            zipOutputStream.closeEntry();
            zipOutputStream.finish();
            return new MockMultipartFile("reportZip", "report.zip", "application/zip", outputStream.toByteArray());
        }
    }

    private static class TestableService extends TestDeviceServiceImpl {
        private int countResult;
        private boolean updateResult = true;
        private TestDevicePO requiredDevice;

        private TestableService(TestDeviceReportStorageService reportStorageService) {
            super(reportStorageService);
        }

        @Override
        public int count(Wrapper<TestDevicePO> queryWrapper) {
            return countResult;
        }

        @Override
        public boolean updateById(TestDevicePO entity) {
            return updateResult;
        }

        @Override
        public TestDevicePO requireNormal(String id) {
            return requiredDevice;
        }
    }
}
