package com.ai.da.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.ai.da.common.config.FileProperties;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.*;
import com.ai.da.common.response.ResultEnum;
import com.ai.da.common.utils.*;
import com.ai.da.mapper.primary.CollectionElementMapper;
import com.ai.da.mapper.primary.GenerateDetailMapper;
import com.ai.da.mapper.primary.GenerateMapper;
import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.*;
import com.ai.da.model.enums.ModelType;
import com.ai.da.model.enums.Module;
import com.ai.da.model.enums.Sex;
import com.ai.da.model.vo.*;
import com.ai.da.python.PythonService;
import com.ai.da.python.vo.DesignPythonItem;
import com.ai.da.python.vo.ImageSegmentation;
import com.ai.da.service.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import io.netty.util.internal.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author yanglei
 * @since 2022-09-30
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementMapper, CollectionElement> implements CollectionElementService {
    private final CollectionElementMapper collectionElementMapper;
//    private final EmailService emailService;
    private final FileProperties fileProperties;
    private final GenerateMapper generateMapper;
    private final GenerateDetailMapper generateDetailMapper;
    private final LibraryModelPointService libraryModelPointService;
    private final LibraryService libraryService;
    private final MinioUtil minioUtil;
    private final PanToneService panToneService;
    private final PythonService pythonService;
    private final RedisUtil redisUtil;
    private final SysFileService sysFileService;
    private final TCollectionElementRelationService tCollectionElementRelationService;
    private final WorkspaceService workspaceService;

    @Value("${minio.bucketName.collectionElement}")
    private String collectionElement;
    @Value("${minio.bucketName.gradient}")
    private String gradientBucketName;
    @Value("${minio.bucketName.users}")
    private String userBucketName;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public CollectionElementVO upload(CollectionElementUploadDTO uploadDTO) {
        long start = System.currentTimeMillis();
        //用户信息
        AuthPrincipalVo userInfo = UserContext.getUserHolder();
        CollectionLevel1TypeEnum level1TypeEnum = CollectionLevel1TypeEnum.uploadOf(uploadDTO.getLevel1Type());
        String name;
        if (Objects.isNull(level1TypeEnum) && !uploadDTO.getLevel1Type().equals(Module.deReconstruction.getValue())) {
            throw new BusinessException("unknown.parameter.level1Type");
        } else if (uploadDTO.getLevel1Type().equals(Module.deReconstruction.getValue())) {
            name = "Construction";
        } else {
            name = level1TypeEnum.getRealName();
        }
        String objectName = userInfo.getId() + "/" + name;
        String path = minioUtil.upload(collectionElement, objectName, uploadDTO.getFile());

        String level2Type = null;
        if (uploadDTO.getLevel1Type().equals(CollectionLevel1TypeEnum.SKETCH_BOARD.getRealName())) {
            if (StringUtil.isNullOrEmpty(uploadDTO.getGender())) {
                throw new BusinessException("gender.cannot.be.empty");
            }
            level2Type = pythonService.getClothCategory(path, uploadDTO.getGender());
        } else if (!StringUtil.isNullOrEmpty(uploadDTO.getLevel2Type())) {
            level2Type = uploadDTO.getLevel2Type();
        }

        //保存element元素
        CollectionElement collectionElement = resolveData(uploadDTO, userInfo, path, level2Type);
        saveOne(collectionElement);
//        if (!StringUtils.isEmpty(uploadDTO.getMoodboardPosition())) {
//            redisUtil.saveMoodboardPosition(collectionElement.getId(), uploadDTO.getMoodboardPosition());
//        }
        CollectionElementVO collectionElementVO = CopyUtil.copyObject(collectionElement, CollectionElementVO.class);
        collectionElementVO.setMinIOPath(collectionElementVO.getUrl());
        collectionElementVO.setUrl(minioUtil.getPreSignedUrl(collectionElementVO.getUrl(), 24 * 60));
        collectionElementVO.setDesignType(DesignTypeEnum.COLLECTION.getRealName());

        long end = System.currentTimeMillis();
        double floor = Math.floor((double) (end - start) / 1000);
        log.info("本次图片上传耗时:{} 毫秒", end - start);
        Long incrementCount = redisUtil.getIncrementCount(RedisUtil.UPLOAD_TIMEOUT_REMINDER_COUNTER);
        if (floor > 5 && incrementCount < 3) {
            // 邮件通知 一天最多通知3次
            log.info("上传超过5秒，发送邮件通知");
            SendEmailUtil.uploadTimeoutReminder(userInfo.getUsername(), String.valueOf(((end - start) / 1000)));
//            emailService.uploadTimeoutReminder(userInfo.getUsername(), String.valueOf(((end - start) / 1000)));
            redisUtil.increaseCount(RedisUtil.UPLOAD_TIMEOUT_REMINDER_COUNTER);
        }
        return collectionElementVO;
    }

    private String calculateFileUrl(CollectionLevel1TypeEnum level1TypeEnum, Long userId) {
        String rootPath = fileProperties.getSys().getPath();
        String day = DateUtil.dateToStr(new Date(), DateUtil.YYYYMM);
        return rootPath + day + "/" + "userFile" + "/collection"
                + "/" + level1TypeEnum.getRealName() + "/" + userId + "/" + UUID.randomUUID().toString();
    }

    private CollectionElement resolveData(CollectionElementUploadDTO uploadDTO, AuthPrincipalVo userInfo, File file) {
        CollectionElement element = CopyUtil.copyObject(uploadDTO, CollectionElement.class);
        element.setAccountId(userInfo.getId());
        element.setCollectionId(0L);
        element.setHasPin((byte) 0);
        String pictureCollectonName = file.getName();
        //获取图片后缀
        String suffix = pictureCollectonName.substring(pictureCollectonName.lastIndexOf("."));
        //获取图片前缀
        String prefix = pictureCollectonName.substring(0, pictureCollectonName.lastIndexOf("."));
        element.setName(DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD));
        element.setUrl(file.getAbsolutePath());
        //按时区计算
        element.setCreateDate(DateUtil.getByTimeZone(uploadDTO.getTimeZone()));
        String linuxDomain = fileProperties.getLinuxDomain();
        if (!StringUtils.isEmpty(linuxDomain)) {
            //linux 系统
            String oldPath = fileProperties.getSys().getPath();
            element.setUrl(file.getAbsolutePath().replace(oldPath, linuxDomain));
        }
        return element;
    }

    private CollectionElement resolveData(CollectionElementUploadDTO uploadDTO, AuthPrincipalVo userInfo, String path, String level2Type) {
        CollectionElement element = CopyUtil.copyObject(uploadDTO, CollectionElement.class);
        element.setAccountId(userInfo.getId());
        element.setCollectionId(0L);
        element.setHasPin((byte) 0);
//        String pictureCollectonName = fileName;
//        //获取图片后缀
//        String suffix = pictureCollectonName.substring(pictureCollectonName.lastIndexOf("."));
//        //获取图片前缀
//        String prefix = pictureCollectonName.substring(0,pictureCollectonName.lastIndexOf("."));
        String originalFilename = uploadDTO.getFile().getOriginalFilename();
        if (originalFilename != null && originalFilename.contains(".")) {
            // 如果文件名包含点号，则去除最后一个点及其后面的内容
            int lastDotIndex = originalFilename.lastIndexOf(".");
            if (lastDotIndex != -1) {
                originalFilename = originalFilename.substring(0, lastDotIndex);
            }
        }
        element.setName(originalFilename);
        element.setUrl(path);
        //按时区计算
        element.setCreateDate(DateUtil.getByTimeZone(uploadDTO.getTimeZone()));
        element.setLevel2Type(level2Type);
        if (Objects.nonNull(uploadDTO.getProjectId())) {
            element.setProjectId(uploadDTO.getProjectId());
            if (CollectionLevel1TypeEnum.SKETCH_BOARD.getRealName().equals(element.getLevel1Type())) {
                element.setLevel3Type(workspaceService.getProjectSexById(uploadDTO.getProjectId()));
            }
        }
//        String linuxDomain = fileProperties.getLinuxDomain();
//        if (!StringUtils.isEmpty(linuxDomain)) {
//            //linux 系统
//            String oldPath = fileProperties.getSys().getPath();
//            element.setUrl(file.getAbsolutePath().replace(oldPath, linuxDomain));
//        }
        return element;
    }

    @Override
    public void delete(Long id) {
        CollectionElement collectionElement = selectById(id);
        if (Objects.isNull(collectionElement)) {
            throw new BusinessException("collectionElement.not.found");
        }
        minioUtil.deleteObject(collectionElement.getUrl());
        collectionElementMapper.deleteById(id);
//        if (!FileUtil.delete(collectionElement.getUrl())) {
//            throw new BusinessException("file deletion failed! ");
//        }
    }

    @Override
    public void batchDelete(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
//        QueryWrapper<CollectionElement> queryWrapper = new QueryWrapper<>();
//        queryWrapper.in("id", ids);
//        CollectionElement collectionElement = new CollectionElement();
//        collectionElement.setCollectionId(0L);
        collectionElementMapper.deleteBatchIds(ids);
    }

    /**
     * 该方法已不再使用
     */
    @Deprecated
    @Override
    public GenerateCollectionItemVO generatePrint(CollectionGeneratePrintDTO generatePrintDTO) {
        Long userId = UserContext.getUserHolder().getId();
        String url1 = null;
        String url2 = null;
        CollectionElement element1 = selectById(generatePrintDTO.getSelect1Id());
        if (Objects.isNull(element1)) {
            Library library1 = libraryService.getById(generatePrintDTO.getSelect1Id());
            if (Objects.isNull(library1)) {
                throw new BusinessException("select1.file.does.not.exist");
            }
            url1 = library1.getUrl();
        } else {
            url1 = element1.getUrl();
        }
        CollectionElement element2 = selectById(generatePrintDTO.getSelect2Id());
        if (Objects.isNull(element2)) {
            Library library2 = libraryService.getById(generatePrintDTO.getSelect2Id());
            if (Objects.isNull(library2)) {
                throw new BusinessException("select2.file.does.not.exist");
            }
            url2 = library2.getUrl();
        } else {
            url2 = element2.getUrl();
        }
        List<String> printPath = Arrays.asList(url1, url2);
        //调取python 接口
        String generateUrl = pythonService.generatePrint(printPath, userId);
        if (StringUtils.isEmpty(generateUrl)) {
            throw new BusinessException("generate.interface.exception");
        }

        // 保存合成信息到generate表
        Generate generate = setGenerate(userId, generatePrintDTO.getTimeZone());
        generateMapper.insert(generate);

        // 保存合成后的信息到generateDetail
        GenerateDetail generateDetail = setGenerateDetail(generate.getId(), generateUrl, generatePrintDTO.getTimeZone());
        generateDetailMapper.insert(generateDetail);

//        CollectionElement element = resolveData(generateUrl, generatePrintDTO.getTimeZone(), userId);
//        if (!this.save(element)) {
//            throw new BusinessException("save.collectionElement.failed");
//        }
//        CollectionGeneratePrintVO collectionGeneratePrint = CopyUtil.copyObject(element, CollectionGeneratePrintVO.class);
//        collectionGeneratePrint.setUrl(minioUtil.getPresignedUrl(generateUrl, 24 * 60));
//        collectionGeneratePrint.setDesignType(DesignTypeEnum.COLLECTION.getRealName());
//        return collectionGeneratePrint;
        return new GenerateCollectionItemVO(generateDetail.getId(),
                minioUtil.getPreSignedUrl(generateUrl, 24 * 60),
                generateDetail.getIsLike().equals((byte) 0) ? Boolean.FALSE : Boolean.TRUE);
    }

    @Override
    public Boolean savePrint(CollectionSavePrintDTO savePrintDTO) {
        //用户信息
        List<CollectionElement> elements = listByIds(savePrintDTO.getPrintId());
        if (CollectionUtils.isEmpty(elements)) {
            throw new BusinessException("collectionElements.not.found");
        }
        return saveLibraryByCollectionElement(elements, savePrintDTO.getTimeZone());
    }

    @Override
    public Boolean saveLibraryByCollectionElement(List<CollectionElement> elements, String timeZone) {
        if (CollectionUtils.isEmpty(elements)) {
            return Boolean.TRUE;
        }
        //获取已存在相同的library
        List<String> md5List = elements.stream().map(CollectionElement::getMd5).collect(Collectors.toList());
        List<Library> existsLibrarys = libraryService.getByMD5List(md5List);
        if (!CollectionUtils.isEmpty(existsLibrarys)) {
            //去重
            List<String> existsMd5Lists = existsLibrarys.stream().map(Library::getMd5).collect(Collectors.toList());
            elements = elements.stream().filter(element -> !existsMd5Lists.contains(element.getMd5())).collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(elements)) {
            //都是重复的
            return Boolean.TRUE;
        }
        String name = DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD);
        List<Library> libraryList = CopyUtil.copyList(elements, Library.class, (o, d) -> {
            if (StringUtil.isNullOrEmpty(o.getLevel2Type())) {
                switch (d.getLevel1Type()) {
                    case "Sketchboard":
                        d.setLevel2Type("Outwear");
                        break;
                    case "Printboard":
                        d.setLevel2Type("Pattern");
                        break;
                }
            } else {
                d.setLevel2Type(o.getLevel2Type());
            }
            d.setCreateDate(DateUtil.getByTimeZone(timeZone));
            d.setName(name);
            d.setId(null);
        });
        if (!libraryService.saveBatch(libraryList)) {
            throw new BusinessException("batch.save.libraryList.failed");
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean saveLibraryByCollectionElement(List<CollectionElement> elements, String timeZone,
                                                  String modelSex, String ageGroup) {
        if (CollectionUtils.isEmpty(elements)) {
            return Boolean.TRUE;
        }
        // 根据MD5去重：保留第一个出现的元素，其余重复MD5的丢弃
        elements = elements.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toMap(
                                CollectionElement::getMd5,
                                e -> e,
                                (e1, e2) -> e1 // 冲突时保留第一个
                        ),
                        map -> new ArrayList<>(map.values())
                ));
        //获取已存在相同的library
        List<String> md5List = elements.stream().map(CollectionElement::getMd5).collect(Collectors.toList());
        List<Library> existsLibrarys = libraryService.getByMD5List(md5List);
        if (!CollectionUtils.isEmpty(existsLibrarys)) {
            //去重
            List<String> existsMd5Lists = existsLibrarys.stream().map(Library::getMd5).collect(Collectors.toList());
            elements = elements.stream().filter(element -> !existsMd5Lists.contains(element.getMd5())).collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(elements)) {
            //都是重复的
            return Boolean.TRUE;
        }
        String name = DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD);
        List<Library> libraryList = CopyUtil.copyList(elements, Library.class, (o, d) -> {
            if (d.getLevel1Type().equals(LibraryLevel1TypeEnum.SKETCH_BOARD.getRealName())) {
                d.setLevel3Type(modelSex);
                d.setAgeGroup(ageGroup);
//                try {
//                    libraryService.processSketchBoards(d.getUrl(), d.getLevel2Type());
//                }catch (Exception e) {
//                    // TODO:暂不处理
//                }
            }
            if (StringUtil.isNullOrEmpty(o.getLevel2Type())) {
                switch (d.getLevel1Type()) {
                    case "Sketchboard":
                        d.setLevel2Type("Outwear");
                        break;
                    case "Printboard":
                        d.setLevel2Type("Pattern");
                        break;
                }
            } else {
                d.setLevel2Type(o.getLevel2Type());
            }
            d.setCreateDate(DateUtil.getByTimeZone(timeZone));
            d.setName(name);
            d.setId(null);
        });
        if (!libraryService.saveBatch(libraryList)) {
            throw new BusinessException("batch.save.libraryList.failed");
        }
        return Boolean.TRUE;
    }

    private CollectionElement resolveData(String path, String timeZone, Long userId) {
//        File file = new File(path);

        String name = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
        CollectionElement element = new CollectionElement();
        element.setAccountId(userId);
        element.setCollectionId(0L);
        element.setName(name);
        element.setLevel1Type(CollectionLevel1TypeEnum.PRINT_BOARD.getRealName());
        element.setUrl(path);
        element.setHasPin((byte) 0);
        try {
            element.setMd5(MD5Utils.encryptFile(minioUtil.download(path)));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        /* catch (MinioException | IOException e) {
            throw new RuntimeException(e);
        }*/

        //按时区计算
        element.setCreateDate(DateUtil.getByTimeZone(timeZone));
        return element;
    }

    /**
     * 验证和处理设计集合元素
     * <p>
     * 功能说明：
     * - 验证设计集合DTO中的各种元素（moodBoards、printBoards、sketchBoards等）
     * - 处理渐变色彩板的base64图片上传到MinIO
     * - 校验PIN数量限制（上衣、下衣、外套各不超过8个）
     * - 验证模特信息和模板点位
     * - 转换并整合来自library和generate的元素数据
     *
     * @param designDTO 设计集合DTO，包含所有设计元素信息
     * @return ValidateElementVO 验证后的元素VO对象，包含处理后的所有元素数据
     * @throws BusinessException 当数据验证失败时抛出业务异常
     */
    @Override
    public ValidateElementVO validateElement(DesignCollectionDTO designDTO) {
        // 将设计DTO转换为验证元素VO对象
        ValidateElementVO elementVO = CopyUtil.copyObject(designDTO, ValidateElementVO.class);

        // 处理颜色板中的渐变色彩
        List<CollectionColorDTO> colorBoards = elementVO.getColorBoards();
        for (CollectionColorDTO colorBoard : colorBoards) {
            if (Objects.nonNull(colorBoard.getGradient())) {
                // 解析base64格式的渐变图片数据
                String colorImg = colorBoard.getGradient().getColorImg();
                String[] parts = colorImg.split(",");
                String imageType = parts[0].split("/")[1].split(";")[0]; // 提取图片类型
                String base64Data = parts[1]; // 提取base64数据

                // 将base64图片上传到MinIO存储
                String gradientMinioUrl = minioUtil.uploadImageFromBase64(gradientBucketName, base64Data, imageType);
                colorBoard.setGradientMinioUrl(gradientMinioUrl);
                colorBoard.getGradient().setColorImg(null); // 清空原始base64数据
                colorBoard.setGradientString(JSON.toJSONString(colorBoard.getGradient())); // 保存渐变配置JSON
            }
        }
        elementVO.setColorBoards(colorBoards);

        // 初始化用于收集各类元素的集合
        List<Long> usedElementIds = elementVO.getUsedElementIds(); // 已使用的元素ID列表
        List<CollectionElement> libraryCollectionElements = elementVO.getLibraryCollectionElements(); // 来自library的元素
        List<CollectionElement> generateCollectionElements = elementVO.getGenerateCollectionElements(); // 来自generate的元素
        // ========== 校验和处理moodBoards（情绪板） ==========
        if (CollectionUtil.isNotEmpty(designDTO.getMoodBoards())) {
            // 校验designType字段是否有效
            validateDesignType(designDTO.getMoodBoards(), "moodBoards");

            // 提取类型为COLLECTION的moodBoard ID列表
            List<Long> moodBoardIds = designDTO.getMoodBoards().stream()
                    .filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName()))
                    .map(DesignCollectionElementDTO::getId)
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(moodBoardIds)) {
                // 从数据库批量查询moodBoard元素
                List<CollectionElement> MoodBoardElements = collectionElementMapper.selectBatchIds(moodBoardIds);
                // 验证查询结果的完整性
                if (CollectionUtil.isEmpty(MoodBoardElements) || MoodBoardElements.size() != moodBoardIds.size()) {
                    throw new BusinessException("get.moodBoards.data.is.mismatch");
                }
                elementVO.setMoodBoardElements(MoodBoardElements);
                usedElementIds.addAll(moodBoardIds); // 记录已使用的元素ID
            }
            // 处理类型为LIBRARY的moodBoard元素
            List<Long> libraryIds = designDTO.getMoodBoards().stream()
                    .filter(f -> f.getDesignType().equals(DesignTypeEnum.LIBRARY.getRealName()))
                    .map(DesignCollectionElementDTO::getId)
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(libraryIds)) {
                List<Library> librarys = libraryService.getByIds(libraryIds);
                // 不严格校验，防止用户在library中删除元素导致对应不上
                if (CollectionUtil.isNotEmpty(librarys)) {
                    libraryCollectionElements.addAll(covertLibrarysToCollections(librarys, null));
                }
            }

            // 处理类型为GENERATE的moodBoard元素
            List<Long> generateIds = designDTO.getMoodBoards().stream()
                    .filter(o -> o.getDesignType().equals((DesignTypeEnum.GENERATE.getRealName())))
                    .map(DesignCollectionElementDTO::getId)
                    .collect((Collectors.toList()));
            if (CollectionUtil.isNotEmpty(generateIds)) {
                List<GenerateDetail> generateDetailList = generateDetailMapper.selectBatchIds(generateIds);
                if (CollectionUtil.isNotEmpty(generateDetailList)) {
                    generateCollectionElements.addAll(covertGeneratesToCollections(generateDetailList, null));
                }
            }
        }

        // ========== 校验和处理printBoards（印花板） ==========
        if (CollectionUtil.isNotEmpty(designDTO.getPrintBoards())) {
            // 校验designType字段是否有效
            validateDesignType(CopyUtil.copyList(designDTO.getPrintBoards(), DesignCollectionElementDTO.class), "printBoards");

            // 提取类型为COLLECTION的printBoard ID列表
            List<Long> printBoardIds = designDTO.getPrintBoards().stream()
                    .filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName()))
                    .map(DesignCollectionPrintElementDTO::getId)
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(printBoardIds)) {
                // 从数据库批量查询printBoard元素
                List<CollectionElement> printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds);
                // 验证查询结果的完整性
                if (CollectionUtil.isEmpty(printBoardElements) || printBoardElements.size() != printBoardIds.size()) {
                    throw new BusinessException("get.printBoards.data.is.mismatch");
                }
                elementVO.setPrintBoardElements(printBoardElements);
                usedElementIds.addAll(printBoardIds); // 记录已使用的元素ID
            }
            // 处理类型为LIBRARY的printBoard元素
            List<Long> libraryIds = designDTO.getPrintBoards().stream()
                    .filter(f -> f.getDesignType().equals(DesignTypeEnum.LIBRARY.getRealName()))
                    .map(DesignCollectionPrintElementDTO::getId)
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(libraryIds)) {
                List<Library> librarys = libraryService.getByIds(libraryIds);
                // 不严格校验，防止用户在library中删除元素导致对应不上
                if (CollectionUtil.isNotEmpty(librarys)) {
                    // 创建ID到printBoard元素的映射，用于保持元素属性
                    Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
                            .stream()
                            .collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
                    libraryCollectionElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap));
                }
            }

            // 处理类型为GENERATE的printBoard元素
            List<Long> generateIds = designDTO.getPrintBoards().stream()
                    .filter(o -> o.getDesignType().equals((DesignTypeEnum.GENERATE.getRealName())))
                    .map(DesignCollectionPrintElementDTO::getId)
                    .collect((Collectors.toList()));
            if (CollectionUtil.isNotEmpty(generateIds)) {
                List<GenerateDetail> generateDetailList = generateDetailMapper.selectBatchIds(generateIds);
                if (CollectionUtil.isNotEmpty(generateDetailList)) {
                    // 创建ID到printBoard元素的映射，用于保持元素属性
                    Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
                            .stream()
                            .collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
                    generateCollectionElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap));
                }
            }
        }

        // ========== 校验和处理sketchBoards（草图板） ==========
        if (CollectionUtil.isNotEmpty(designDTO.getSketchBoards())) {
            // 校验PIN数量限制：上衣、下衣、外套各不超过8个
            long topNum = 0;
            long bottomNum = 0;
            long outerwearNum = 0;
            //去掉与性别不符合的线稿元素 - 使用 Iterator 安全移除元素
            Iterator<CollectionSketchDTO> sketchIterator = designDTO.getSketchBoards().iterator();
            while (sketchIterator.hasNext()) {
                try {
                    CollectionSketchDTO sketchBoard = sketchIterator.next();
                    String level2Type = sketchBoard.getLevel2Type();
                    String level3Type = null;
                    if ("Collection".equals(sketchBoard.getDesignType())) {
                        level3Type = collectionElementMapper.selectOne(new LambdaQueryWrapper<CollectionElement>().eq(CollectionElement::getId, sketchBoard.getSketchBoardId()).select(CollectionElement::getLevel3Type)).getLevel3Type();
                    } else if ("Library".equals(sketchBoard.getDesignType())) {
                        level3Type = libraryService.getById(sketchBoard.getSketchBoardId()).getLevel3Type();
                    } else if ("Generate".equals(sketchBoard.getDesignType())) {
                        GenerateDetail generateDetail = generateDetailMapper.selectById(sketchBoard.getSketchBoardId());
                        assert generateDetail != null;
                        Generate generate = generateMapper.selectById(generateDetail.getGenerateId());
                        assert generate != null;
                        level3Type = GenerateServiceImpl.extractGender(generate.getGenerateType());
                    }
                    //判断性别和当前project性别是否一致，不一致则移除
                    assert level3Type != null;
                    if (!level3Type.equalsIgnoreCase(designDTO.getModelSex())) {
                        sketchIterator.remove();
                    }
                } catch (Exception e) {
                    continue;
                }

            }
            //再次判断草图板数量，如果为null，则跳出当前if
            if (CollectionUtil.isNotEmpty(designDTO.getSketchBoards())) {

                // 根据模特性别统计不同类型的PIN数量
                if (designDTO.getModelSex().equals(Sex.FEMALE.getValue())) {
                    // 女性模特：统计连衣裙和上衣类PIN数量
                    topNum = designDTO.getSketchBoards().stream()
                            .filter(skecth -> skecth.getIsPin() == 1
                                    && DesignPythonItem.DRESS_BLOUSE.contains(skecth.getLevel2Type())).count();
                    // 女性模特：统计裙子和裤子类PIN数量
                    bottomNum = designDTO.getSketchBoards().stream()
                            .filter(skecth -> skecth.getIsPin() == 1
                                    && DesignPythonItem.SKIRT_TROUSERS.contains(skecth.getLevel2Type())).count();
                } else if (designDTO.getModelSex().equals(Sex.MALE.getValue())) {
                    // 男性模特：统计上衣类PIN数量
                    topNum = designDTO.getSketchBoards().stream()
                            .filter(skecth -> skecth.getIsPin() == 1
                                    && DesignPythonItem.TOPS.contains(skecth.getLevel2Type())).count();
                    // 男性模特：统计下装类PIN数量
                    bottomNum = designDTO.getSketchBoards().stream()
                            .filter(skecth -> skecth.getIsPin() == 1
                                    && DesignPythonItem.BOTTOMS.contains(skecth.getLevel2Type())).count();
                }
                // 统计外套类PIN数量（男女通用）
                outerwearNum = designDTO.getSketchBoards().stream()
                        .filter(skecth -> skecth.getIsPin() == 1
                                && DesignPythonItem.OUTWEAR.contains(skecth.getLevel2Type())).count();

                // 验证PIN数量限制：每个类别不能超过8个
                if (topNum > 8 || bottomNum > 8 || outerwearNum > 8) {
                    throw new BusinessException("the.number.of.PIN.top.or.bottom.or.outerwear.sketchBoard.cannot.be.more.than.8", ResultEnum.PROMPT.getCode());
                }

                // 校验designType字段不能为空
                Boolean result = designDTO.getSketchBoards().stream()
                        .filter(mood -> StringUtils.isEmpty(mood.getDesignType()))
                        .findFirst().isPresent();
                if (result) {
                    throw new BusinessException("sketchBoards.designType.cannot.be.empty");
                }
                List<CollectionElement> sketchBoardElements = new ArrayList<>();
                // 提取类型为COLLECTION的sketchBoard ID列表
                List<Long> sketchBoardIds = designDTO.getSketchBoards().stream()
                        .filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName()))
                        .map(CollectionSketchDTO::getSketchBoardId)
                        .collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(sketchBoardIds)) {
                    // 从数据库查询验证sketchBoard元素是否存在
                    sketchBoardElements = collectionElementMapper.selectBatchIds(sketchBoardIds);

                    if (CollectionUtil.isEmpty(sketchBoardElements) || sketchBoardElements.size() != sketchBoardIds.size()) {
                        throw new BusinessException("get.sketchBoards.data.is.mismatch");
                    }

                    // 记录已使用的元素ID
                    usedElementIds.addAll(sketchBoardIds);
                }

                // 处理类型为LIBRARY的sketchBoard元素
                List<Long> libraryIds = designDTO.getSketchBoards().stream()
                        .filter(f -> f.getDesignType().equals(DesignTypeEnum.LIBRARY.getRealName()))
                        .map(CollectionSketchDTO::getSketchBoardId)
                        .collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(libraryIds)) {
                    List<Library> librarys = libraryService.getByIds(libraryIds);
                    // 不严格校验，防止用户在library中删除元素导致对应不上
                    if (CollectionUtil.isNotEmpty(librarys)) {
                        // 创建ID到sketchBoard元素的映射，用于保持元素属性
                        Map<Long, CollectionSketchDTO> idToMap = designDTO.getSketchBoards()
                                .stream()
                                .collect(Collectors.toMap(CollectionSketchDTO::getSketchBoardId, v -> v));
                        List<CollectionElement> librarysToCollections = covertLibrarysToCollections(librarys, idToMap);
                        libraryCollectionElements.addAll(librarysToCollections);
                        sketchBoardElements.addAll(librarysToCollections);
                    }
                }

                // 处理类型为GENERATE的sketchBoard元素
                List<Long> generateIds = designDTO.getSketchBoards().stream()
                        .filter(o -> o.getDesignType().equals((DesignTypeEnum.GENERATE.getRealName())))
                        .map(CollectionSketchDTO::getSketchBoardId)
                        .collect((Collectors.toList()));
                if (CollectionUtil.isNotEmpty(generateIds)) {
                    List<GenerateDetail> generateDetailList = generateDetailMapper.selectBatchIds(generateIds);
                    if (CollectionUtil.isNotEmpty(generateDetailList)) {
                        // 创建ID到sketchBoard元素的映射，用于保持元素属性
                        Map<Long, CollectionSketchDTO> idToMap = designDTO.getSketchBoards()
                                .stream()
                                .collect(Collectors.toMap(CollectionSketchDTO::getSketchBoardId, v -> v));
                        List<CollectionElement> generatesToCollections = covertGeneratesToCollections(generateDetailList, idToMap);
                        generateCollectionElements.addAll(generatesToCollections);
                        sketchBoardElements.addAll(generatesToCollections);
                    }
                }
                // 设置验证通过的sketchBoard元素到结果对象
                elementVO.setSketchBoardElements(sketchBoardElements);
            }
        }
        //校验marketingSketch
        // 2023.12版本去掉了这个入参
/*        if (CollectionUtil.isNotEmpty(designDTO.getMarketingSketchs())) {
            //校验designType
            validateDesignType(designDTO.getMarketingSketchs(),"marketingSketchs");
            List<Long> printBoardIds = designDTO.getMarketingSketchs().stream()
                    .filter(f ->f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName()))
                    .map(DesignCollectionElementDTO::getId)
                    .collect(Collectors.toList());
            if(!CollectionUtils.isEmpty(printBoardIds)){
                List<CollectionElement> marketingSketchElements = collectionElementMapper.selectBatchIds(printBoardIds);
                Assert.isTrue(CollectionUtil.isNotEmpty(marketingSketchElements)
                        && marketingSketchElements.size() == printBoardIds.size(), "get marketingSketch data is mismatch");
                elementVO.setMarketingSketchElements(marketingSketchElements);
                usedElementIds.addAll(printBoardIds);
            }
            //library
            List<Long> libraryIds = designDTO.getMarketingSketchs().stream()
                    .filter(f ->f.getDesignType().equals(DesignTypeEnum.LIBRARY.getRealName()))
                    .map(DesignCollectionElementDTO::getId)
                    .collect(Collectors.toList());
            if(!CollectionUtils.isEmpty(libraryIds)){
                List<Library> librarys = libraryService.getByIds(libraryIds);
                //不校验了防止用户在library删除 对应不上
                if(CollectionUtil.isNotEmpty(librarys)){
                    libraryCollectionElements.addAll(covertLibrarysToCollections(librarys,null));
                }
            }
        }*/

        // ========== 校验控制生成类型参数 ==========
        // 验证singleOverall参数（单品/整体生成控制）
        SingleOverallEnum singleOverall = SingleOverallEnum.of(designDTO.getSingleOverall());
        if (Objects.isNull(singleOverall)) {
            log.error("未知singleOverall param：{}", designDTO.getSingleOverall());
            log.info("入参 designDTO => {}", designDTO);
            throw new BusinessException("unknown.parameter.singleOverall");
        }

        // 如果是单品生成模式，需要验证switchCategory参数
        if (SingleOverallEnum.SINGLE.equals(singleOverall)) {
            SwitchCategoryEnum switchCategory = SwitchCategoryEnum.of(designDTO.getSwitchCategory());
            if (Objects.isNull(switchCategory)) {
                log.error("未知switchCategory：{}", designDTO.getSwitchCategory());
                log.info("入参 designDTO => {}", designDTO);
                throw new BusinessException("unknown.parameter.switchCategory");
            }
        }

        // ========== 校验和处理模特信息 ==========
        if (!CollectionUtils.isEmpty(designDTO.getMannequins())) {
            // 处理多个模特的情况 - 使用 Iterator 安全移除元素
            List<DesignLibraryModelPointVO> designLibraryModelPointVOList = new ArrayList<>();
            Iterator<MannequinDTO> mannequinIterator = designDTO.getMannequins().iterator();
            while (mannequinIterator.hasNext()) {
                MannequinDTO mannequin = mannequinIterator.next();
                if (mannequin.getType().equals("System")) {
                    // 处理系统模特
                    SysFileVO byId = sysFileService.getById(mannequin.getId());
                    if (Objects.isNull(byId)) {
                        log.info("未知模特：{}：{}", mannequin.getId(), "System");
                        throw new BusinessException("model.not.found");
                    }
                    if (!designDTO.getModelSex().equalsIgnoreCase(byId.getLevel2Type())) {
                        mannequinIterator.remove();
                        continue;
                    }
                    // 注释：女性模特的风格设置（已禁用）
//                    if (!StringUtils.isEmpty(byId.getLevel3Type()) && byId.getLevel2Type().equals("Female")) {
//                        elementVO.setStyle(byId.getLevel3Type());
//                    }
                    // 获取模特点位信息并计算模板点位
                    LibraryModelPoint modelPoint = libraryModelPointService.getByRelationId(byId.getId(), mannequin.getType());
                    designLibraryModelPointVOList.add(calculateTemplatePointTemplate(modelPoint, 700, 320, byId.getUrl()));
                } else {
                    // 处理用户自定义模特
                    Library byId = libraryService.getById(mannequin.getId());
                    //如果当前模特性别和project模特性别不一致，则为此模特进行design
                    if (!designDTO.getModelSex().equalsIgnoreCase(byId.getLevel2Type())) {
                        mannequinIterator.remove();
                        continue;
                    }
                    LibraryModelPoint modelPoint = libraryModelPointService.getByRelationId(byId.getId(), mannequin.getType());
                    designLibraryModelPointVOList.add(calculateTemplatePointTemplate(modelPoint, byId.getHigh(), byId.getWidth(), byId.getUrl()));
                }
            }
            elementVO.setMannequins(designLibraryModelPointVOList);
        } else {
            // 处理单个模特的情况（通过templateId指定）
            if (!StringUtils.isEmpty(designDTO.getModelType())) {
                if (designDTO.getModelType().equals(ModelType.LIBRARY.getValue())) {
                    // 处理用户自定义模特
                    Library byId = libraryService.getById(designDTO.getTemplateId());
                    LibraryModelPoint modelPoint = libraryModelPointService.getByRelationId(byId.getId(), designDTO.getModelType());
                    elementVO.setDesignLibraryModelPoint(calculateTemplatePointTemplate(modelPoint, byId.getHigh(), byId.getWidth(), byId.getUrl()));
                } else if (designDTO.getModelType().equals(ModelType.SYSTEM.getValue())) {
                    // 处理系统模特
                    SysFileVO byId = sysFileService.getById(designDTO.getTemplateId());
                    // 设置女性模特的风格类型
                    if (Objects.nonNull(byId) && !StringUtils.isEmpty(byId.getLevel3Type()) && byId.getLevel2Type().equals("Female")) {
//                        elementVO.setStyle(byId.getLevel3Type());
                    }
                    LibraryModelPoint modelPoint = libraryModelPointService.getByRelationId(byId.getId(), designDTO.getModelType());
                    elementVO.setDesignLibraryModelPoint(calculateTemplatePointTemplate(modelPoint, 700, 320, byId.getUrl()));
                }
            }
        }

        // ========== 设置最终的验证结果参数 ==========
        // 设置模特性别
        elementVO.setModelSex(designDTO.getModelSex());
        // 设置请求ID列表（用于批量查询）
        elementVO.setRequestIdList(designDTO.getRequestIdList());
        // 设置设计方案数量，默认为8个
        if (null != designDTO.getDesignNum()) {
            elementVO.setDesignNum(designDTO.getDesignNum());
        } else {
            elementVO.setDesignNum(8);
        }
        // 设置品牌信息（如果存在）
        if (null != designDTO.getBrandId()) {
            elementVO.setBrandId(designDTO.getBrandId());  // 设置品牌ID
            elementVO.setBrandScale(designDTO.getBrandScale());  // 设置品牌比例
        }

        //设置风格
        String style = workspaceService.getStyleByProjectId(designDTO.getProjectId());
        elementVO.setStyle(style);

        // 返回验证结果对象
        return elementVO;
    }

    @Override
    public DesignLibraryModelPointVO calculateTemplatePoint(LibraryModelPoint modelPoint, Integer high, Integer width, String templateUrl) {
        DesignLibraryModelPointVO libraryModelPoint = new DesignLibraryModelPointVO();
        libraryModelPoint.setHandLeft(calculateTemplatePointOne(modelPoint.getHandLeft(), high, width));
        libraryModelPoint.setHandRight(calculateTemplatePointOne(modelPoint.getHandRight(), high, width));
        libraryModelPoint.setShoulderLeft(calculateTemplatePointOne(modelPoint.getShoulderLeft(), high, width));
        libraryModelPoint.setShoulderRight(calculateTemplatePointOne(modelPoint.getShoulderRight(), high, width));
        libraryModelPoint.setWaistbandLeft(calculateTemplatePointOne(modelPoint.getWaistbandLeft(), high, width));
        libraryModelPoint.setWaistbandRight(calculateTemplatePointOne(modelPoint.getWaistbandRight(), high, width));
        libraryModelPoint.setTemplateUrl(templateUrl);
        return libraryModelPoint;
    }

    @Override
    public DesignLibraryModelPointVO calculateTemplatePointTemplate(LibraryModelPoint modelPoint, Integer high, Integer width, String templateUrl) {
        DesignLibraryModelPointVO libraryModelPoint = new DesignLibraryModelPointVO();
//        LibraryModelPoint template = libraryModelPointService.getById(96L);
//        libraryModelPoint.setHandLeft(calculateTemplatePointOne(template.getHandLeft(),752,564));
//        libraryModelPoint.setHandRight(calculateTemplatePointOne(template.getHandRight(),752,564));
//        libraryModelPoint.setShoulderLeft(calculateTemplatePointOne(template.getShoulderLeft(),752,564));
//        libraryModelPoint.setShoulderRight(calculateTemplatePointOne(template.getShoulderRight(),752,564));
//        libraryModelPoint.setWaistbandLeft(calculateTemplatePointOne(template.getWaistbandLeft(),752,564));
//        libraryModelPoint.setWaistbandRight(calculateTemplatePointOne(template.getWaistbandRight(),752,564));
//        libraryModelPoint.setTemplateUrl("aida-mannequins/model_1693218345.2714432.png");
        libraryModelPoint.setHandLeft(calculateTemplatePointOne(modelPoint.getHandLeft(), high, width));
        libraryModelPoint.setHandRight(calculateTemplatePointOne(modelPoint.getHandRight(), high, width));
        libraryModelPoint.setShoulderLeft(calculateTemplatePointOne(modelPoint.getShoulderLeft(), high, width));
        libraryModelPoint.setShoulderRight(calculateTemplatePointOne(modelPoint.getShoulderRight(), high, width));
        libraryModelPoint.setWaistbandLeft(calculateTemplatePointOne(modelPoint.getWaistbandLeft(), high, width));
        libraryModelPoint.setWaistbandRight(calculateTemplatePointOne(modelPoint.getWaistbandRight(), high, width));
        libraryModelPoint.setTemplateUrl(templateUrl);
        libraryModelPoint.setRelationId(modelPoint.getRelationId());
        libraryModelPoint.setRelationType(modelPoint.getModelType());
        return libraryModelPoint;
    }

    private List<BigDecimal> calculateTemplatePointOne(String template, Integer high, Integer width) {
        List<BigDecimal> originRatioList = JSON.parseObject(template, List.class);
        originRatioList.set(0, originRatioList.get(0).multiply(BigDecimal.valueOf(width)));
        originRatioList.set(1, originRatioList.get(1).multiply(BigDecimal.valueOf(high)));
        return originRatioList;
    }

    private List<CollectionElement> covertLibrarysToCollections(List<Library> libraries, Map<Long, CollectionSketchDTO> idToMap) {
        return CopyUtil.copyList(libraries, CollectionElement.class, (o, d) -> {
            if (null != idToMap) {
                CollectionSketchDTO sketchDTO = idToMap.get(o.getId());
                d.setLevel2Type(sketchDTO.getLevel2Type());
                d.setHasPin(sketchDTO.getIsPin());
            }
        });
    }

    private List<CollectionElement> covertGeneratesToCollections(List<GenerateDetail> generateDetailList, Map<Long, CollectionSketchDTO> idToMap) {
        return CopyUtil.copyList(generateDetailList, CollectionElement.class, (o, d) -> {
            Generate byId = generateMapper.selectById(o.getGenerateId());
            d.setAccountId(byId.getAccountId());
            d.setLevel1Type(byId.getLevel1Type());
            d.setCreateDate(Date.from(o.getCreateDate().atZone(ZoneId.systemDefault()).toInstant()));
            if (null != idToMap) {
                CollectionSketchDTO sketchDTO = idToMap.get(o.getId());
                d.setLevel2Type(sketchDTO.getLevel2Type());
                d.setHasPin(sketchDTO.getIsPin());
            }
        });
    }

    private List<CollectionElement> covertLibrarysToPrintCollections(List<Library> libraries, Map<Long, DesignCollectionPrintElementDTO> idToMap) {
        return CopyUtil.copyList(libraries, CollectionElement.class, (o, d) -> {
            if (null != idToMap) {
                DesignCollectionPrintElementDTO printDTO = idToMap.get(o.getId());
                d.setHasPin(printDTO.getIsPin());
            }
        });
    }

    private List<CollectionElement> covertGeneratesToPrintCollections(List<GenerateDetail> generateDetailList, Map<Long, DesignCollectionPrintElementDTO> idToMap) {
        return CopyUtil.copyList(generateDetailList, CollectionElement.class, (o, d) -> {
            Generate byId = generateMapper.selectById(o.getGenerateId());
            d.setAccountId(byId.getAccountId());
            d.setLevel1Type(byId.getLevel1Type());
            if (!StringUtils.isEmpty(byId.getLevel2Type())) {
                d.setLevel2Type(byId.getLevel2Type());
            }
            d.setCreateDate(Date.from(o.getCreateDate().atZone(ZoneId.systemDefault()).toInstant()));
            if (null != idToMap) {
                DesignCollectionPrintElementDTO printDTO = idToMap.get(o.getId());
                d.setHasPin(printDTO.getIsPin());
            }
        });
    }

    private void validateDesignType(List<DesignCollectionElementDTO> collectionElements, String msg) {
        Boolean result = collectionElements.stream().filter(mood -> StringUtils.isEmpty(mood.getDesignType())).findFirst().isPresent();
        if (result) {
            throw new BusinessException(msg + ".designType.cannot.be.empty");
        }
    }

    @Override
    public void editSketchBoardsElement(ValidateElementVO elementVO, List<CollectionSketchDTO> sketchBoards) {
        if (CollectionUtil.isNotEmpty(sketchBoards)) {
            List<CollectionSketchDTO> collect = sketchBoards.stream().filter(o -> o.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName())).collect(Collectors.toList());
            collect.forEach(sketchBoard -> {
                CollectionElement collectionElement = CopyUtil.copyObject(sketchBoard, CollectionElement.class);
                collectionElement.setHasPin(sketchBoard.getIsPin());
                collectionElement.setId(sketchBoard.getSketchBoardId());
                collectionElementMapper.updateById(collectionElement);
            });
            List<Long> sketchBoardIds = collect.stream().map(CollectionSketchDTO::getSketchBoardId).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(sketchBoardIds)) {
                List<CollectionElement> sketchBoardElements = collectionElementMapper.selectBatchIds(sketchBoardIds);
                elementVO.setSketchBoardElements(sketchBoardElements);
            } else {
                elementVO.setSketchBoardElements(new ArrayList<>());
            }
        }
    }

    @Override
    public void editPrintBoardsElement(ValidateElementVO elementVO, List<DesignCollectionPrintElementDTO> printBoards) {
        if (CollectionUtil.isNotEmpty(printBoards)) {
            List<DesignCollectionPrintElementDTO> collect = printBoards.stream().filter(o -> o.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName())).collect(Collectors.toList());
            collect.forEach(printBoard -> {
                CollectionElement collectionElement = CopyUtil.copyObject(printBoard, CollectionElement.class);
                collectionElement.setHasPin(Objects.isNull(printBoard.getIsPin()) ? 0 : printBoard.getIsPin());
                collectionElement.setId(printBoard.getId());
                collectionElementMapper.updateById(collectionElement);
            });
            List<Long> printBoardIds = collect.stream().map(DesignCollectionPrintElementDTO::getId).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(printBoardIds)) {
                List<CollectionElement> printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds);
                elementVO.setPrintBoardElements(printBoardElements);
            } else {
                elementVO.setPrintBoardElements(new ArrayList<>());
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void relationCollection(List<Long> elementIds, Long collectionId) {
        if (CollectionUtils.isEmpty(elementIds) || null == collectionId) {
            return;
        }
        QueryWrapper<CollectionElement> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(CollectionElement::getId, elementIds);
        CollectionElement element = new CollectionElement();
        element.setCollectionId(collectionId);
        //批量关联
        collectionElementMapper.update(element, queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<CollectionElementVO> saveColorBoard(List<CollectionColorDTO> colorBoards, Long collectionId, String timeZone) {
        //用户信息
        AuthPrincipalVo userInfo = UserContext.getUserHolder();
        List<CollectionElement> colorElements = resolveColorData(colorBoards, userInfo, collectionId, timeZone);
        if (!this.saveBatch(colorElements)) {
            throw new BusinessException("batch.save.colorElements.failed");
        }
        return CopyUtil.copyList(colorElements, CollectionElementVO.class);
    }

    @Override
    public void refreshHistoryData() {
        //幂等
        if (!CollectionUtils.isEmpty(tCollectionElementRelationService.getByCollectionId(1083L))) {
            return;
        }
        // 分页数据
        QueryWrapper<CollectionElement> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("id");
        PageQueryBaseVo pageQuery = new PageQueryBaseVo();
        pageQuery.setPage(1);
        pageQuery.setSize(200);
        while (true) {
            IPage<CollectionElement> page = getBaseMapper().selectPage(
                    new Page<>(pageQuery.getPage(), pageQuery.getSize()), queryWrapper);
            List<CollectionElement> list = page.getRecords();
            if (CollectionUtils.isEmpty(list)) {
                break;
            }

            //保存
            List<TCollectionElementRelation> relations = list.stream().map(element ->
                            TCollectionElementRelation.builder().elementId(element.getId())
                                    .collectionId(element.getCollectionId()).createDate(new Date()).build())
                    .collect(Collectors.toList());
            tCollectionElementRelationService.saveBatch(relations);
            pageQuery.setPage(pageQuery.getPage() + 1);
            log.info("refreshHistoryData###process###page###" + pageQuery.getPage());
        }
    }

    private List<CollectionElement> resolveColorData(List<CollectionColorDTO> colorBoards, AuthPrincipalVo userInfo, Long collectionId, String timeZone) {
        List<CollectionElement> elements = Lists.newArrayList();
        colorBoards.forEach(color -> {
            CollectionElement element = new CollectionElement();
            element.setAccountId(userInfo.getId());
            element.setCollectionId(collectionId);
            if (StringUtils.isEmpty(color.getName())) {
                element.setName(null);
            } else {
                element.setName(color.getId() + "_" + color.getName() + "_" + color.getTcx());
            }
            element.setLevel1Type(CollectionLevel1TypeEnum.COLOR_BOARD.getRealName());
            element.setHasPin((byte) 0);
            element.setMd5("0");
            element.setColorRgb(color.getRgbValue());
            //按时区计算
            element.setCreateDate(DateUtil.getByTimeZone(timeZone));
            if (Objects.nonNull(color.getGradient())) {
                color.getGradient().setColorImg(null);
            }
            element.setGradientString(JSON.toJSONString(color.getGradient()));
            elements.add(element);
        });
        return elements;
    }

    @Override
    public List<CollectionElement> getByCollectionId(Long collectionId) {
//        List<Long> elementIds = tCollectionElementRelationService.getByCollectionId(collectionId);
//        if (CollectionUtils.isEmpty(elementIds)) {
//            return null;
//        }
        QueryWrapper<CollectionElement> queryWrapper = new QueryWrapper<>();
//        queryWrapper.in("id", elementIds);
        queryWrapper.lambda().eq(CollectionElement::getCollectionId, collectionId);
        return collectionElementMapper.selectList(queryWrapper);
    }

    @Override
    public List<CollectionElement> getByOnlyCollectionId(Long collectionId) {
        QueryWrapper<CollectionElement> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("collection_id", collectionId);
        return collectionElementMapper.selectList(queryWrapper);
    }

    private CollectionElement selectById(Long id) {
        QueryWrapper<CollectionElement> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        return collectionElementMapper.selectOne(queryWrapper);
    }

    private Boolean exists(Long id) {
        QueryWrapper<CollectionElement> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        return collectionElementMapper.exists(queryWrapper);
    }

    private boolean saveOne(CollectionElement collectionElement) {
        if (collectionElementMapper.insert(collectionElement) <= 0) {
            throw new BusinessException("save.collectionElement.failed");
        }
        return Boolean.TRUE;
    }

    @Override
    public CollectionElement editLevel2Type(Long elementId, String level2Type, String designType) {
        CollectionElement collectionElement = new CollectionElement();

        if (!Objects.isNull(elementId)) {
            if (!StringUtil.isNullOrEmpty(designType)) {
                switch (designType) {
                    case "collection":
                        collectionElement = collectionElementMapper.selectById(elementId);
                        if (StringUtil.isNullOrEmpty(collectionElement.getLevel2Type()) || !(collectionElement.getLevel2Type()).equals(level2Type)) {
                            collectionElement.setLevel2Type(level2Type);
                            updateById(collectionElement);
                        }
                        break;
                    case "library":
                        Library libraryElement = libraryService.getById(elementId);
                        if (!Objects.isNull(libraryElement)) {
                            if (StringUtil.isNullOrEmpty(libraryElement.getLevel2Type()) || !(libraryElement.getLevel2Type()).equals(level2Type)) {
                                libraryElement.setLevel2Type(level2Type);
                                libraryService.updateById(libraryElement);
                            }
                            BeanUtils.copyProperties(libraryElement, collectionElement);
                        }
                        break;
                }
            } else {
                log.error("designType cannot be empty");
                throw new BusinessException("element source type cannot be empty!");
            }
        } else {
            return null;
        }
        return collectionElement;
    }

    @Override
    public List<CollectionElement> getByProjectId(Long projectId) {
        QueryWrapper<CollectionElement> qw = new QueryWrapper<>();
        qw.lambda().eq(CollectionElement::getProjectId, projectId);
        List<CollectionElement> collectionElementList = collectionElementMapper.selectList(qw);
        if (CollectionUtils.isEmpty(collectionElementList)) {
            return new ArrayList<>();
        }
        return collectionElementList;
    }

    private Generate setGenerate(Long userId, String timeZone) {
        Generate generate = new Generate();
        generate.setAccountId(userId);
        generate.setLevel1Type(CollectionLevel1TypeEnum.PRINT_BOARD.getRealName());
        generate.setGenerateType("synthesis");
//        generate.setModelName("Image Synthesis Model");
        generate.setCreateDate(DateUtil.getByTimeZone(timeZone));
        return generate;
    }

    private GenerateDetail setGenerateDetail(Long generateId, String url, String timeZone) {
        GenerateDetail generateDetail = new GenerateDetail();
        generateDetail.setGenerateId(generateId);
        generateDetail.setUrl(url);
        String md5;
        try {
            md5 = MD5Utils.encryptFile(minioUtil.download(url));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }/*catch (MinioException | IOException e) {
            throw new RuntimeException(e);
        }*/
        // 通过MD5来确认当前图片是否有被like过，避免重复like
        List<Map<String, Long>> libraryIds = generateDetailMapper.getLibraryIdThroughMD5(md5, CollectionLevel1TypeEnum.PRINT_BOARD.getRealName());
        if (libraryIds.isEmpty()) {
            generateDetail.setIsLike((byte) 0);
        } else {
            generateDetail.setIsLike((byte) 1);
            generateDetail.setLibraryId(libraryIds.get(0).get("library_id"));
        }
        generateDetail.setMd5(md5);
        generateDetail.setCreateDate(LocalDateTime.now());

        return generateDetail;
    }

    // 对于上传图片或者从library选择的图片进行图片分割
    public List<CollectionElementVO> selectedImageSeg(List<MultipartFile> files, Long id, String type, String sourceType) {
        Long accountId = UserContext.getUserHolder().getId();
        List<CollectionElementVO> resp = new ArrayList<>();
        List<ImageSegmentation.ImageDate> imageDates = new ArrayList<>();

        boolean isUploadMode = !files.isEmpty();
        Library library = null;
        CollectionElement collectionElement = null;

        // 判断是否是上传的图片
        if (isUploadMode) {
            String objectName = accountId + "/ImageSegment/input";
            for (MultipartFile file : files) {
                String md5 = MD5Utils.encryptFile(file);
                String segmentedResult = redisUtil.getFromString(RedisUtil.IMAGE_SEGMENTATION + type + ":" + md5);
                // 判断上传的图片是否有已分割的数据
                if (StringUtil.isNullOrEmpty(segmentedResult)) {
                    String path = minioUtil.upload(userBucketName, objectName, file);
                    ImageSegmentation.ImageDate imageDate = new ImageSegmentation().new ImageDate();
                    imageDate.setImage_url(path);
                    imageDate.setImage_type(type);
                    imageDates.add(imageDate);
                } else {
                    ImageSegmentation.ImageDate imageData = JSONObject.parseObject(segmentedResult, ImageSegmentation.ImageDate.class);
                    resp.add(createCollectionElementVO(accountId, null, null, imageData.getImage_url(), imageData.getClothing_url()));
                }
            }
        } else if (Objects.nonNull(id) && sourceType.equals("Library")) {
            library = libraryService.getById(id);
            // 判断从library中选择的图片是否有分割数据
            if (Objects.isNull(library)) {
                throw new BusinessException("library.not.found");
            }
            if (StringUtil.isNullOrEmpty(library.getSegmentedData())) {
                ImageSegmentation.ImageDate imageDate = new ImageSegmentation().new ImageDate();
                imageDate.setImage_url(library.getUrl());
                imageDate.setImage_type(type);
                imageDates.add(imageDate);
            } else {
                List<String> restoredList = Arrays.asList(library.getSegmentedData().split(","));
                resp.add(createCollectionElementVO(accountId, id, library.getLevel1Type(), library.getUrl(), restoredList));
            }
        } else if (Objects.nonNull(id) && sourceType.equals("Upload")) {
            collectionElement = collectionElementMapper.selectById(id);
            // 判断id对应的数据是否存在
            if (Objects.isNull(collectionElement)) {
                throw new BusinessException("get.file.failed");
            }
            // 上传的图片分割数据没存（原因：上传的数据一般不会被再次使用，存储意义不大；上传存储表中数据复杂，添加字段，浪费多）
            ImageSegmentation.ImageDate imageDate = new ImageSegmentation().new ImageDate();
            imageDate.setImage_url(collectionElement.getUrl());
            imageDate.setImage_type(type);
            imageDates.add(imageDate);
        }

        // 处理需要分割的图片
        if (!imageDates.isEmpty()) {
            // 准备图片分割的参数
            ImageSegmentation imageSegmentation = new ImageSegmentation();
            imageSegmentation.setUser_id(accountId);
            imageSegmentation.setImage_data(imageDates);
            // 图片分割
            List<ImageSegmentation.ImageDate> segmented = pythonService.imageSegmentation(imageSegmentation);
            // 处理图片分割结果
            for (ImageSegmentation.ImageDate imageData : segmented) {
                if (isUploadMode) {
                    // 上传的图片需要添加到redis中存一天
                    String key = RedisUtil.IMAGE_SEGMENTATION + type + ":" +
                            MD5Utils.encryptFile
                                    (minioUtil.getPreSignedUrl
                                            (imageData.getImage_url(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false);
                    redisUtil.addToString(key, new Gson().toJson(imageData), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
                    resp.add(createCollectionElementVO(accountId, null, null, imageData.getImage_url(), imageData.getClothing_url()));
                } else if (sourceType.equals("Library")) {
                    // 从library中选择的图片需要更新数据库中对应图片的分割数据
                    String segmentedData = String.join(",", imageData.getClothing_url());
                    library.setSegmentedData(segmentedData);
                    library.setUpdateDate(new Date());
                    libraryService.updateById(library);
                    resp.add(createCollectionElementVO(accountId, id, library.getLevel1Type(), library.getUrl(), imageData.getClothing_url()));
                } else {
                    resp.add(createCollectionElementVO(accountId, id, collectionElement.getLevel1Type(), collectionElement.getUrl(), imageData.getClothing_url()));
                }
            }
        }
        return resp;
    }

    // 准备返回数据
    private CollectionElementVO createCollectionElementVO(Long accountId, Long id, String level1Type,
                                                          String imageUrl, List<String> clothingUrls) {
        CollectionElementVO vo = new CollectionElementVO();
        vo.setAccountId(accountId);
        vo.setId(id);
        vo.setLevel1Type(level1Type);
        vo.setUrl(minioUtil.getPreSignedUrl(imageUrl, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
        vo.setMinIOPath(imageUrl);

        List<String> segUrls = new ArrayList<>();
        for (String seg : clothingUrls) {
            segUrls.add(minioUtil.getPreSignedUrl(seg, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
        }
        vo.setSegmentedImages(segUrls);
        return vo;
    }

    public void updateElementLevel2Type(Long elementId, String level2Type) {
        CollectionElement collectionElement = baseMapper.selectById(elementId);
        if (Objects.nonNull(collectionElement)) {
            collectionElement.setLevel2Type(level2Type);
            collectionElement.setUpdateDate(new Date());
            baseMapper.updateById(collectionElement);
        }
    }

}
