package cn.iocoder.yudao.module.pms.controller.admin.ly.util;

import cn.iocoder.yudao.module.pms.controller.admin.ly.vo.DeviceDataExcelDTO;
import cn.iocoder.yudao.module.pms.controller.admin.vo.DeviceVO;
import cn.iocoder.yudao.module.pms.dal.mysql.TDDeviceMapper;
import com.alibaba.excel.EasyExcel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * EasyExcel同步导出工具类（终极修复：适配所有EasyExcel版本，解决泛型不匹配问题）
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ExcelExportUtil {

    private static final int BATCH_SIZE = 1000000;
    private final TDDeviceMapper tdDeviceMapper;

    public void exportDeviceDataSync(
            HttpServletResponse response,
            String deviceCode,
            Timestamp startTime,
            Timestamp endTime,
            String identity
    ) {
        try {
            // 1. 入参校验
            if (Objects.isNull(startTime) || Objects.isNull(endTime) || StringUtils.isBlank(deviceCode)) {
                throw new RuntimeException("设备编码和时间范围不能为空");
            }
            if (startTime.after(endTime)) {
                throw new RuntimeException("开始时间不能晚于结束时间");
            }

            // 2. 设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            DateTimeFormatter fileNameFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
            String startStr = startTime.toLocalDateTime().format(fileNameFormatter);
            String endStr = endTime.toLocalDateTime().format(fileNameFormatter);
            String fileName = URLEncoder.encode("设备数据_" + deviceCode + "_" + startStr + "_" + endStr, StandardCharsets.UTF_8.toString());
            response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + fileName + ".xlsx");
            response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Expires", "0");

            // 3. 核心修复：强转泛型为Supplier<Collection<?>>，适配EasyExcel API
            Supplier<Collection<?>> dataSupplier = new DeviceDataSupplier(deviceCode, startTime, endTime, identity);

            EasyExcel.write(response.getOutputStream(), DeviceDataExcelDTO.class)
                    .sheet("设备数采数据")
                    .doWrite(dataSupplier); // 此时参数完全匹配

            log.info("设备{}数据导出成功，时间范围：{} - {}", deviceCode, startTime, endTime);
        } catch (Exception e) {
            log.error("设备{}数据导出失败", deviceCode, e);
            throw new RuntimeException("数据导出失败：" + e.getMessage(), e);
        }
    }

    /**
     * 供给型函数：明确实现Supplier<Collection<?>>，解决泛型不匹配
     */
    private class DeviceDataSupplier implements Supplier<Collection<?>> {
        private String deviceCode;
        private Timestamp currentCursor;
        private final Timestamp endTime;
        private final String identity;
        private boolean hasMoreData = true;
        private boolean isFirstQuery = true;

        public DeviceDataSupplier(String deviceCode, Timestamp startTime, Timestamp endTime, String identity) {
            this.deviceCode = deviceCode;
            this.currentCursor = startTime;
            this.endTime = endTime;
            this.identity = identity;
        }

        @Override
        public Collection<?> get() {
            if (!hasMoreData) {
                return new ArrayList<>();
            }

            try {
                // 分批查询数据
                List<DeviceVO> deviceVOs;
                Timestamp queryStart = currentCursor;
                if (!isFirstQuery) {
                    queryStart = new Timestamp(currentCursor.getTime() + 1);
                }

                if (Objects.nonNull(identity) && StringUtils.isNotBlank(identity)) {
                    deviceVOs = tdDeviceMapper.selectAllBtTimeAndidentifierAll(
                            deviceCode, identity, queryStart, endTime, BATCH_SIZE);
                } else {
                    deviceVOs = tdDeviceMapper.selectAllBtTime(
                            deviceCode, queryStart, endTime, BATCH_SIZE);
                }

                log.debug("设备{}分批查询，游标：{}，查询到{}条", deviceCode, queryStart, deviceVOs.size());

                // 判断是否有更多数据
                if (deviceVOs == null || deviceVOs.isEmpty()) {
                    hasMoreData = false;
                    return new ArrayList<>();
                }
                if (deviceVOs.size() < BATCH_SIZE) {
                    hasMoreData = false;
                } else {
                    currentCursor = deviceVOs.get(deviceVOs.size() - 1).getTs();
                }
                isFirstQuery = false;

                // 转换为DTO
                DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                return deviceVOs.stream().map(vo -> {
                    DeviceDataExcelDTO dto = new DeviceDataExcelDTO();
                    dto.setDeviceCode(deviceCode);
                    dto.setTs(vo.getTs().toLocalDateTime().format(timeFormatter));
                    dto.setIdentity(vo.getIdentity());
                    dto.setValue(String.valueOf(vo.getLogValue()));
                    return dto;
                }).collect(Collectors.toList());

            } catch (Exception e) {
                log.error("分批查询失败", e);
                hasMoreData = false;
                throw new RuntimeException("分批查询数据失败：" + e.getMessage(), e);
            }
        }
    }
}