package hw.tagApi.service.service.impl;

import java.io.ByteArrayInputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import hw.tagApi.common.exception.ServiceException;
import hw.tagApi.common.utils.DateUtils;
import hw.tagApi.common.utils.StringUtils;
import hw.tagApi.common.utils.poi.ExcelUtil;
import hw.tagApi.common.utils.uuid.IdGenerator;
import hw.tagApi.service.constant.ApiConstants;
import hw.tagApi.service.domain.ApiContent;
import hw.tagApi.service.domain.ImportParams;
import hw.tagApi.service.domain.ImportResult;
import hw.tagApi.service.utils.TagExcelUtil;
import hw.tagApi.service.utils.httpClientUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import hw.tagApi.service.mapper.HwTagRecordMapper;
import hw.tagApi.service.domain.HwTagRecord;
import hw.tagApi.service.service.IHwTagRecordService;
import org.springframework.transaction.annotation.Transactional;

import static hw.tagApi.service.utils.TagExcelUtil.extractImportParams;
import static hw.tagApi.service.utils.TagExcelUtil.startsWithCheck;

/**
 * 标签记录Service业务层处理
 *
 * @author Yinq
 * @date 2025-04-15
 */
@Service
public class HwTagRecordServiceImpl implements IHwTagRecordService {

    private static final Logger log = LoggerFactory.getLogger(HwTagRecordServiceImpl.class);

    @Autowired
    private HwTagRecordMapper hwTagRecordMapper;

    private static final int BATCH_SIZE = 100;

    /**
     * 查询标签记录
     *
     * @param rId 标签记录主键
     * @return 标签记录
     */
    @Override
    public HwTagRecord selectHwTagRecordByRId(Long rId) {
        return hwTagRecordMapper.selectHwTagRecordByRId(rId);
    }

    /**
     * 查询标签记录列表
     *
     * @param hwTagRecord 标签记录
     * @return 标签记录
     */
    @Override
    public List<HwTagRecord> selectHwTagRecordList(HwTagRecord hwTagRecord) {
        return hwTagRecordMapper.selectHwTagRecordList(hwTagRecord);
    }

    /**
     * 新增标签记录
     *
     * @param hwTagRecord 标签记录
     * @return 结果
     */
    @Override
    public int insertHwTagRecord(HwTagRecord hwTagRecord) {
        hwTagRecord.setCreateTime(DateUtils.getNowDate());
        return hwTagRecordMapper.insertHwTagRecord(hwTagRecord);
    }

    /**
     * 修改标签记录
     *
     * @param hwTagRecord 标签记录
     * @return 结果
     */
    @Override
    public int updateHwTagRecord(HwTagRecord hwTagRecord) {
        initTagRecord(hwTagRecord);
        return hwTagRecordMapper.updateHwTagRecord(hwTagRecord);
    }

    /**
     * 初始化hwTagRecord
     *
     * @param hwTagRecord hwTagRecord
     */
    public void initTagRecord(HwTagRecord hwTagRecord) {
        hwTagRecord.setRId(IdGenerator.nextId());
        hwTagRecord.setCreateTime(DateUtils.getNowDate());
    }

    /**
     * 批量删除标签记录
     *
     * @param rIds 需要删除的标签记录主键
     * @return 结果
     */
    @Override
    public int deleteHwTagRecordByRIds(Long[] rIds) {
        return hwTagRecordMapper.deleteHwTagRecordByRIds(rIds);
    }

    /**
     * 删除标签记录信息
     *
     * @param rId 标签记录主键
     * @return 结果
     */
    @Override
    public int deleteHwTagRecordByRId(Long rId) {
        return hwTagRecordMapper.deleteHwTagRecordByRId(rId);
    }

    /**
     * 根据TID更新标签记录
     *
     * @param record 标签记录
     */
    @Override
    public void updateHwTagRecordByTID(HwTagRecord record) {
        hwTagRecordMapper.updateHwTagRecordByTID(record);
    }

    /**
     * 批量插入标签记录
     *
     * @param batchList 标签记录列表
     */
    @Override
    public void batchInsertHwTagRecord(List<HwTagRecord> batchList) {
        hwTagRecordMapper.batchInsertHwTagRecord(batchList);
    }

    /**
     * 查询接口根据条件查询标签记录
     *
     * @param selectTagRecord （HwTagRecord）
     * @return HwTagRecord
     */
    @Override
    public HwTagRecord selectHwTagRecordData(HwTagRecord selectTagRecord) {
        List<HwTagRecord> tagRecordList = hwTagRecordMapper.selectHwTagRecordList(selectTagRecord);
        if (!tagRecordList.isEmpty()) {
            HwTagRecord tagRecord = tagRecordList.get(0);
            tagRecord.setQueryResultsNumber(tagRecordList.size());
            return tagRecord;
        }
        return null;
    }

    /**
     * 根据TID查询标签记录
     *
     * @param tId TID
     */
    @Override
    public HwTagRecord selectHwTagRecordByTID(String tId) {
        return hwTagRecordMapper.selectHwTagRecordByTxId(tId);
    }

    /**
     * 批量查询已存在的TID
     *
     * @param tids TID列表
     * @return 已存在的TID集合
     */
    @Override
    public Set<String> selectExistingTids(List<String> tids) {
        if (tids == null || tids.isEmpty()) {
            return new HashSet<>();
        }
        return new HashSet<>(hwTagRecordMapper.selectExistingTids(tids));
    }

    /**
     * 批量查询已存在的EPC
     *
     * @param epcs EPC列表
     * @return 已存在的EPC集合
     */
    @Override
    public List<HwTagRecord> selectExistingEpcs(List<String> epcs) {
        if (epcs == null || epcs.isEmpty()) {
            return new ArrayList<>();
        }
        return hwTagRecordMapper.selectExistingEpcs(epcs);
    }

    /**
     * 将Excel数据转换为HwTagRecord列表
     *
     * @param orderInfo 订单信息
     * @param tagList   标签列表
     * @param params    导入参数
     * @return HwTagRecord列表
     */
    private List<HwTagRecord> convertExcelDataToTagRecords(Map<String, String> orderInfo, List<Map<String, String>> tagList, ImportParams params) {
        List<HwTagRecord> records = new ArrayList<>();
        Date nowDate = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        try {
            for (Map<String, String> tagData : tagList) {
                HwTagRecord record = new HwTagRecord();

                // 设置订单信息
                String lot = orderInfo.getOrDefault("orderNo", "").replace("Lot", "");
                record.setOrderCode(lot);
                record.setBatchNumber(orderInfo.getOrDefault("batchNo", ""));
                record.setOperatorId(orderInfo.getOrDefault("operatorId", ""));
                Date processTime = orderInfo.containsKey("processTime") && !orderInfo.get("processTime").isEmpty() ?
                        sdf.parse(orderInfo.get("processTime")) : nowDate;
                record.setProcessingTime(processTime);
                record.setTotalQuantity(Long.valueOf(orderInfo.getOrDefault("totalCount", "0")));

                // 设置标签数据
                record.setModelCode(params.getModel());
                record.setTagSequence(tagData.get("序号"));
                record.setTId(tagData.get("TID").replace("-", ""));
                record.setEpc(tagData.get("EPC").replace("-", ""));
                record.setPassword(tagData.get("密码"));
                record.setTestResult(tagData.get("测试结果"));
                record.setTestValue(tagData.get("测试值"));
                record.setReferenceValue(tagData.get("参考值"));
                Date testingTime = tagData.containsKey("测试时间") ?
                        sdf.parse(tagData.get("测试时间")) : nowDate;
                record.setTestingTime(testingTime);
                record.setFileName(params.getFileName());
                record.setDestinationInfo(params.getDestinationInfo());
                record.setLibraryFlag(params.getLibraryFlag());
                record.setIntendedUse(params.getIntendedUse());
                record.setRemark(params.getRemark());

                records.add(record);
            }
        } catch (Exception e) {
            log.error("转换Excel数据失败: {}", e.getMessage(), e);
            throw new ServiceException("转换Excel数据失败: " + e.getMessage());
        }

        return records;
    }

    /**
     * 导入数据
     *
     * @param apiContent 包含导入数据的ApiContent对象
     * @return 处理后的ApiContent对象
     */
    @Transactional(rollbackFor = Exception.class)
    public ApiContent importData(ApiContent apiContent) {
        Map<String, Object> fields = apiContent.getFields();
        Map<String, Object> resFields = new HashMap<>();
        int successCount = 0;
        int failCount = 0;
        apiContent.setFields(resFields);
        try {
            // 获取导入参数
            ImportParams params = extractImportParams(fields);
            log.info("开始处理文件，URL: {}", params.getFileUrl());

            // 获取文件数据
            byte[] fileData = httpClientUtils.getByteArrayFromUrl(params.getFileUrl());
            if (fileData == null) {
                handleImportError(resFields, "获取文件失败");
                return apiContent;
            }

            // 解析Excel数据
            try (ByteArrayInputStream bis = new ByteArrayInputStream(fileData)) {
                Map<String, Object> excelResult = TagExcelUtil.parseTagExcel(bis);
                if (!excelResult.containsKey("tagList")) {
                    handleImportError(resFields, "解析Excel文件失败，未找到标签数据");
                    return apiContent;
                }
                String templateType = String.valueOf(excelResult.get("templateType"));

                List<HwTagRecord> tagRecordList = new ArrayList<>();
                if (templateType.equals(ApiConstants.IMPORT_TEMPLATE_ONE)) {
                    ByteArrayInputStream inputStream = new ByteArrayInputStream(fileData);
                    ExcelUtil<HwTagRecord> utils = new ExcelUtil<>(HwTagRecord.class);
                    tagRecordList = utils.importExcel(inputStream);
                    convertExcelDataTemplateOne(tagRecordList, params);
                }

                if (templateType.equals(ApiConstants.IMPORT_TEMPLATE_TWO) || templateType.equals(ApiConstants.IMPORT_TEMPLATE_THREE)
                        || templateType.equals(ApiConstants.IMPORT_TEMPLATE_FOUR)) {
                    // 获取订单信息和标签数据
                    Map<String, String> orderInfo = (Map<String, String>) excelResult.get("orderInfo");
                    List<Map<String, String>> tagList = (List<Map<String, String>>) excelResult.get("tagList");
                    // 转换Excel数据为HwTagRecord列表
                    tagRecordList = convertExcelDataToTagRecords(orderInfo, tagList, params);
                }
                // 检查TID是否存在
                if (tagRecordList.stream().anyMatch(tag -> !StringUtils.hasText(tag.getTId()))) {
                    throw new IllegalArgumentException("解析excel错误：存在缺少TID的数据！");
                }
                // 处理标签数据
                ImportResult result = processTagList(tagRecordList, params);
                successCount = result.getSuccessCount();
                failCount = result.getFailCount();
                List<String> errorMessages = new ArrayList<>(result.getErrorMessages());

                // 设置导入结果
                if (failCount > 0) {
                    throw new ServiceException(String.format(params.getImportMode() + "失败：%d条。失败原因：%s",
                            failCount, String.join("; ", errorMessages)));
                } else {
                    resFields.put(ApiConstants.IMPORT_STATUS, "导入成功");
                    resFields.put(ApiConstants.DESCRIPTION, String.format(params.getImportMode() + "成功导入%d条数据", successCount));
                }
            }
        } catch (Exception e) {
            log.error("处理文件失败: {}", e.getMessage(), e);
            handleImportError(resFields, e.getMessage());
            return apiContent;
        }

        return apiContent;
    }

    /**
     * 处理模板一的Excel数据
     * 根据导入参数更新标签记录
     *
     * @param tagRecordList 标签记录列表
     * @param params        导入参数
     */
    private void convertExcelDataTemplateOne(List<HwTagRecord> tagRecordList, ImportParams params) {
        for (HwTagRecord tagRecord : tagRecordList) {
            // 设置型号
            if (StringUtils.hasText(params.getModel())) {
                tagRecord.setModelCode(params.getModel());
            }
            // 设置文件名
            if (StringUtils.hasText(params.getFileName())) {
                tagRecord.setFileName(params.getFileName());
            }
            // 设置去向信息
            if (StringUtils.hasText(params.getDestinationInfo())) {
                tagRecord.setDestinationInfo(params.getDestinationInfo());
            }
            // 设置是否在库
            if (StringUtils.hasText(params.getLibraryFlag())) {
                tagRecord.setLibraryFlag(params.getLibraryFlag());
            }
            // 设置计划用途
            if (StringUtils.hasText(params.getIntendedUse())) {
                tagRecord.setIntendedUse(params.getIntendedUse());
            }
            // 设置备注
            if (StringUtils.hasText(params.getRemark())) {
                tagRecord.setRemark(params.getRemark());
            }
        }
    }

    /**
     * 处理导入错误
     */
    private void handleImportError(Map<String, Object> resFields, String errorMessage) {
        log.error("导入错误: {}", errorMessage);
        resFields.put(ApiConstants.IMPORT_STATUS, "导入失败");
        resFields.put(ApiConstants.DESCRIPTION, errorMessage);
    }

    /**
     * 处理标签列表
     */
    private ImportResult processTagList(List<HwTagRecord> records, ImportParams params) {
        ImportResult result = new ImportResult();
        List<HwTagRecord> batchList = new ArrayList<>();

        // 获取已存在的TID和EPC
        Set<String> existingTids = getExistingTids(records, params);
        List<HwTagRecord> existingEpcs = getExistingEpcs(records, params);

        for (HwTagRecord record : records) {
            try {
                // 校验标签数据
                if (!validateTagRecord(record, params, existingTids, existingEpcs, result)) {
                    continue;
                }

                // 处理更新或插入
                if (handleRecordUpdateOrInsert(record, params, batchList, result)) {
                    continue;
                }

                // 批量插入处理
                if (batchList.size() >= BATCH_SIZE && result.getFailCount() == 0) {
                    processBatchInsert(batchList, result);
                }
            } catch (Exception e) {
                handleTagProcessingError(record, e, result);
                return result;
            }
        }

        // 处理剩余的批量数据
        if (!batchList.isEmpty() && result.getFailCount() == 0) {
            processBatchInsert(batchList, result);
        }

        return result;
    }

    /**
     * 获取已存在的TID
     */
    private Set<String> getExistingTids(List<HwTagRecord> records, ImportParams params) {
        List<String> allTids = records.stream()
                .map(HwTagRecord::getTId)
                .collect(Collectors.toList());
        return this.selectExistingTids(allTids);
    }

    /**
     * 获取已存在的EPC
     */
    private List<HwTagRecord> getExistingEpcs(List<HwTagRecord> records, ImportParams params) {
        List<String> allEpcs = records.stream()
                .map(HwTagRecord::getEpc)
                .collect(Collectors.toList());
        return this.selectExistingEpcs(allEpcs);
    }

    /**
     * 校验标签数据
     */
    private boolean validateTagRecord(HwTagRecord record, ImportParams params,
                                      Set<String> existingTids, List<HwTagRecord> existingEpcs,
                                      ImportResult result) {
        // 校验TID长度
        if (record.getTId().length() != ApiConstants.TE_LENGTH) {
            String errorMsg = String.format("TID长度校验失败！预期长度：24，实际长度：%d，TID值：%s",
                    record.getTId().length(), record.getTId());
            result.getErrorMessages().add(errorMsg);
            result.incrementFailCount();
            return false;
        }

        // 校验EPC长度
        if (params.getEPCNumberCheckLength() > 0 &&
                record.getEpc().length() != params.getEPCNumberCheckLength()) {
            String errorMsg = String.format("EPC长度校验失败！预期长度：%d，实际长度：%d，EPC值：%s",
                    params.getEPCNumberCheckLength(), record.getEpc().length(), record.getEpc());
            result.getErrorMessages().add(errorMsg);
            result.incrementFailCount();
            return false;
        }

        // 校验TID前缀
        if (!params.getTIDCheck().isEmpty() &&
                !startsWithCheck(record.getTId(), params.getTIDCheck())) {
            String errorMsg = String.format("TID校验失败！TID校验：%s，实际TID：%s",
                    params.getTIDCheck(), record.getTId());
            result.getErrorMessages().add(errorMsg);
            result.incrementFailCount();
            return false;
        }

        // 校验EPC前缀
        if (!params.getEPCCheck().isEmpty() &&
                !startsWithCheck(record.getEpc(), params.getEPCCheck())) {
            String errorMsg = String.format("EPC校验失败！EPC校验：%s，实际EPC：%s",
                    params.getEPCCheck(), record.getEpc());
            result.getErrorMessages().add(errorMsg);
            result.incrementFailCount();
            return false;
        }

        // 校验TID唯一性
        boolean tIdFlag = ApiConstants.MODE_ADD.equals(params.getImportMode()) ||
                ApiConstants.MODE_ADD_NO_CHECK.equals(params.getImportMode());
        if (tIdFlag && existingTids.contains(record.getTId())) {
            String errorMsg = String.format("TID已存在：%s", record.getTId());
            result.getErrorMessages().add(errorMsg);
            result.incrementFailCount();
            return false;
        }

        // 校验更新模式下的TID存在性
        if ((ApiConstants.MODE_UPDATE.equals(params.getImportMode()) ||
                ApiConstants.MODE_UPDATE_NO_CHECK.equals(params.getImportMode())) &&
                !existingTids.contains(record.getTId())) {
            String errorMsg = String.format("数据库未查询到TID:%s,无法%s",
                    record.getTId(), params.getImportMode());
            result.getErrorMessages().add(errorMsg);
            result.incrementFailCount();
            return false;
        }

        // 校验EPC唯一性
        boolean epcCheckFlag = ApiConstants.MODE_ADD.equals(params.getImportMode()) ||
                ApiConstants.MODE_UPDATE.equals(params.getImportMode());
        if (epcCheckFlag) {
            boolean isAddMode = ApiConstants.MODE_ADD.equals(params.getImportMode());
            boolean isUpdateMode = ApiConstants.MODE_UPDATE.equals(params.getImportMode());
            Set<String> epcSet = isAddMode ?
                    existingEpcs.stream().map(HwTagRecord::getEpc).collect(Collectors.toSet()) :
                    Collections.emptySet();
            Map<String, List<HwTagRecord>> epcMap = isUpdateMode ?
                    existingEpcs.stream().collect(Collectors.groupingBy(HwTagRecord::getEpc)) :
                    Collections.emptyMap();
            boolean conflict = false;
            if (isAddMode && epcSet.contains(record.getEpc())) {
                conflict = true;
            } else if (isUpdateMode) {
                List<HwTagRecord> sameEpcs = epcMap.get(record.getEpc());
                if (sameEpcs != null) {
                    conflict = sameEpcs.stream()
                            .anyMatch(r -> !r.getTId().equals(record.getTId()));
                }
            }
            if (conflict) {
                String errorMsg = String.format("EPC已存在：%s", record.getEpc());
                result.getErrorMessages().add(errorMsg);
                result.incrementFailCount();
                return false;
            }
        }

        return true;
    }

    /**
     * 处理记录更新或插入
     */
    private boolean handleRecordUpdateOrInsert(HwTagRecord record, ImportParams params,
                                               List<HwTagRecord> batchList, ImportResult result) {
        if ((ApiConstants.MODE_UPDATE.equals(params.getImportMode()) ||
                ApiConstants.MODE_UPDATE_NO_CHECK.equals(params.getImportMode())) &&
                result.getFailCount() == 0) {
            this.updateHwTagRecordByTID(record);
            result.incrementSuccessCount();
            return true;
        }
        record.setRId(IdGenerator.nextId());
        batchList.add(record);
        return false;
    }

    /**
     * 处理批量插入
     */
    private void processBatchInsert(List<HwTagRecord> batchList, ImportResult result) {
        this.batchInsertHwTagRecord(batchList);
        log.info("批量插入{}条数据成功", batchList.size());
        result.addSuccessCount(batchList.size());
        batchList.clear();
    }

    /**
     * 处理标签处理错误
     */
    private void handleTagProcessingError(HwTagRecord record, Exception e, ImportResult result) {
        String errorMsg = String.format("处理数据失败 - TID: %s, 错误: %s", record.getTId(), e.getMessage());
        log.error(errorMsg);
        result.getErrorMessages().add(errorMsg);
        result.incrementFailCount();
    }
}
