package cn.cslg.pas.service.impl;

import cn.cslg.pas.common.model.PersonnelVO;
import cn.cslg.pas.common.model.dto.*;
import cn.cslg.pas.common.model.vo.PathStructureNameVO;
import cn.cslg.pas.common.model.vo.StructurePictureVO;
import cn.cslg.pas.common.model.vo.StructureVO;
import cn.cslg.pas.common.utils.CacheUtils;
import cn.cslg.pas.common.utils.FileUtils;
import cn.cslg.pas.common.utils.SecurityUtils.LoginUtils;
import cn.cslg.pas.common.utils.ThrowException;
import cn.cslg.pas.domain.Project;
import cn.cslg.pas.domain.Structure;
import cn.cslg.pas.domain.asso.AssoStructurePicture;
import cn.cslg.pas.exception.XiaoShiException;
import cn.cslg.pas.mapper.asso.AssoStructurePatentMapper;
import cn.cslg.pas.mapper.asso.AssoStructurePictureMapper;
import cn.cslg.pas.mapper.StructureMapper;
import cn.cslg.pas.service.IStructureService;
import cn.cslg.pas.service.ProjectService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 架构的Service层接口实现类
 *
 * @Author chenyu
 * @Date 2023/3/10
 */
@RequiredArgsConstructor
@Slf4j
@Service
public class StructureServiceImpl extends ServiceImpl<StructureMapper, Structure> implements IStructureService {
    private final StructureMapper structureMapper;
    private final AssoStructurePictureMapper assoStructurePictureMapper;
    private final AssoStructurePatentMapper assoStructurePatentMapper;
    private final ProjectService projectService;
    private final CacheUtils cacheUtils;
    private final LoginUtils loginUtils;
    private final FileUtils fileUtils;

    /**
     * 新增架构
     *
     * @param structureAddNewDTO 新增架构DTO对象
     * @param files              架构图片
     */
    @Override
    public void addNew(StructureAddNewDTO structureAddNewDTO, List<MultipartFile> files) {
        log.info("开始处理【新增架构】的业务，参数为：{}, {}", structureAddNewDTO, files);

        Integer parentId = structureAddNewDTO.getParentId();
        String structureName = structureAddNewDTO.getStructureName();
        Integer productId = structureAddNewDTO.getProductId();
        //检查名称是否被占用(检查当前架构父级下是否有同名架构)
        log.info("检查名称是否被占用(检查当前父级下是否有同名架构)");
        int count = structureMapper.countByparentIdAndStructureName(productId, parentId, structureName, null);
        if (count > 0) {
            String message = "新增架构失败，当前父级下已存在【" + structureName + "】，请尝试更换名称";
            log.info("{}", message);
            throw new XiaoShiException(message);
        }

        //DTO对象赋值给实体类
        Structure structure = new Structure();
        BeanUtils.copyProperties(structureAddNewDTO, structure);

        PersonnelVO personnelVO = cacheUtils.getLoginUser(loginUtils.getId());
        structure
                .setCreatePersonId(personnelVO.getId())
                .setCreatePersonName(personnelVO.getName());

        //数据入架构表(此时数据暂不含路径path)
        log.info("数据入架构表");
        int rows = structureMapper.insert(structure);
        if (rows != 1) {
            String message = "新增架构失败，服务器忙请稍后再次尝试!";
            log.info("{}", message);
            throw new XiaoShiException(message);
        }

        //数据入架构表后，取出id
        Integer structureId = structure.getId();
        String parentPath = structureAddNewDTO.getParentPath();
        String path;
        if (parentPath != null) {
            path = parentPath + "," + structureId;
        } else {
            path = "0," + structureId;
        }
        structure.setPath(path);
        log.info("更新架构表");
        rows = structureMapper.update(structure);
        if (rows != 1) {
            String message = "新增架构失败，服务器忙请稍后再次尝试!";
            log.info("{}", message);
            throw new XiaoShiException(message);
        }

        //如果前端有传来架构图片，则图片上传服务器并获取图片信息，将图片信息入架构图片关联表
        if (files != null && files.size() > 0) {
            ArrayList<AssoStructurePicture> assoStructurePictures = new ArrayList<>();
            for (MultipartFile file : files) {
                UploadFileDTO fileDTO = fileUtils.uploadFile(file);
                AssoStructurePicture assoStructurePicture = new AssoStructurePicture()
                        .setStructureId(structureId)
                        .setName(fileDTO.getName())
                        .setSuffix(fileDTO.getExtName())
                        .setUrl(fileDTO.getPath());
                assoStructurePictures.add(assoStructurePicture);
            }
            rows = assoStructurePictureMapper.insertBatch(assoStructurePictures);
            if (rows != assoStructurePictures.size()) {
                String message = "新增架构失败，数据入架构图片关联表失败，服务器忙请稍后再次尝试!";
                log.info("{}", message);
                throw new XiaoShiException(message);
            }
        }

        log.info("新增架构完成");

    }

    /**
     * 修改架构
     *
     * @param structureUpdateDTO 修改架构DTO对象
     * @param files              架构图片
     */
    @Override
    public void update(StructureUpdateDTO structureUpdateDTO, List<MultipartFile> files) {
        log.info("开始处理【修改架构】的业务，参数为：{}, {}", structureUpdateDTO, files);

        //检查尝试修改的数据是否存在
        Integer structureId = structureUpdateDTO.getId();
        log.info("检查尝试修改的数据是否存在");
        Structure queryResult = structureMapper.getStandardById(structureId);
        if (queryResult == null) {
            String message = "修改架构失败，尝试访问的数据已不存在";
            log.info("{}", message);
            throw new XiaoShiException(message);
        }

        //获取该架构原名称、父级id和原路径
        Integer oldParentId = queryResult.getParentId();
        String oldPath = queryResult.getPath();
        //获取DTO中架构现名称、父级id和父路径
        Integer newParentId = structureUpdateDTO.getParentId();
        String newPath = structureUpdateDTO.getParentPath() + "," + structureId;
        //获取DTO中该架构当前名称
        String newStructureName = structureUpdateDTO.getStructureName();
        Integer productId = structureUpdateDTO.getProductId();

        //检查名称是否被占用(检查当前尝试修改的架构父级下是否有同名架构)
        log.info("检查名称是否被占用(检查当前尝试修改的架构的新父级下是否有同名架构)");
        int count = structureMapper.countByparentIdAndStructureName(productId, newParentId, newStructureName, structureId);
        if (count > 0) {
            String message = "修改架构失败，当前尝试修改的架构父级下已存在【" + newStructureName + "】，请尝试更换名称";
            log.info("{}", message);
            throw new XiaoShiException(message);
        }

        //DTO对象赋值给实体类
        Structure structure = new Structure();
        BeanUtils.copyProperties(structureUpdateDTO, structure);

        //如果新路径和原路径不一样，则表示架构更换了父级
        if (!oldParentId.equals(newParentId)) {
            structure.setParentId(newParentId);
            structure.setPath(newPath);
            //还要将当前架构下所有子级架构的路径也都更新
            //根据当前架构原来的路径查询所有子级架构
            log.info("根据当前架构原来的路径查询所有子级架构");
            List<StructureVO> structures = structureMapper.selectByFindInSetPath(structureId + "");
            for (StructureVO n : structures) {
                //如果是当前修改的架构则跳过当前遍历
                if (n.getId().equals(structureId)) {
                    continue;
                }
                String path = n.getPath();
                //从它的父级架构路径(即当前修改的架构)的原来的路径的长度的位置处截取后面路径
                String behindString = path.substring(oldPath.length());
                path = newPath + behindString;
                n.setPath(path);
                Structure structure2 = new Structure();
                BeanUtils.copyProperties(n, structure2);
                log.info("修改当前已更换父级的架构的子级架构路径path");
                int rows = structureMapper.update(structure2);
                if (rows != 1) {
                    String message = "修改架构失败，修改当前已更换父级的架构的子级架构路径path失败，服务器忙请稍后再次尝试!";
                    log.info("{}", message);
                    throw new XiaoShiException(message);
                }
            }
        }

        //架构表修改数据
        log.info("架构表修改数据");
        int rows = structureMapper.update(structure);
        if (rows != 1) {
            String message = "修改架构失败，服务器忙请稍后再次尝试!";
            log.info("{}", message);
            throw new XiaoShiException(message);
        }

        //架构图片关联表删除数据(若有原有图片被删除)
        //根据架构id查询出所有原有图片
        log.info("根据架构id查询出所有原有图片");
        List<StructurePictureVO> structurePictures = assoStructurePictureMapper.selectByStructureId(structureId);
        ArrayList<Integer> oldPictureIds = new ArrayList<>();
        for (StructurePictureVO structurePicture : structurePictures) {
            Integer oldPictureId = structurePicture.getId();
            oldPictureIds.add(oldPictureId);
        }
        //从DTO中获取传过来的原有图片
        List<StructurePictureUpdateDTO> pictures = structureUpdateDTO.getPictures();
        ArrayList<Integer> newOldPictureIds = new ArrayList<>();
        for (StructurePictureUpdateDTO picture : pictures) {
            Integer newOldPictureId = picture.getId();
            newOldPictureIds.add(newOldPictureId);
        }
        //图片id集合去重，保留下来的即被删除的图片id
        oldPictureIds.removeAll(newOldPictureIds);
        if (oldPictureIds.size() > 0) {
            log.info("架构图片关联表删除数据");
            rows = assoStructurePictureMapper.deleteByIds(oldPictureIds);
            if (rows != oldPictureIds.size()) {
                String message = "修改架构失败，架构图片关联表删除数据失败，服务器忙请稍后再次尝试!";
                log.info("{}", message);
                throw new XiaoShiException(message);
            }
        }

        //数据入架构图片关联表(若有新增新图片)
        if (files != null && files.size() != 0) {
            ArrayList<AssoStructurePicture> assoStructurePictures = new ArrayList<>();
            for (MultipartFile file : files) {
                UploadFileDTO fileDTO = fileUtils.uploadFile(file);
                AssoStructurePicture assoStructurePicture = new AssoStructurePicture()
                        .setStructureId(structureId)
                        .setName(fileDTO.getName())
                        .setSuffix(fileDTO.getExtName())
                        .setUrl(fileDTO.getPath());
                assoStructurePictures.add(assoStructurePicture);
            }
            log.info("数据入架构图片关联表");
            rows = assoStructurePictureMapper.insertBatch(assoStructurePictures);
            if (rows != files.size()) {
                String message = "修改架构失败，数据入架构图片关联表失败，服务器忙请稍后再次尝试!";
                log.info("{}", message);
                throw new XiaoShiException(message);
            }
        }

        log.info("修改架构完成");

    }

    /**
     * 分页查询架构树
     *
     * @param structureQueryPageDTO 分页信息
     * @return 返回查询到的数据
     */
    @Override
    public StructureVO query(StructureQueryPageDTO structureQueryPageDTO) {
        log.info("开始处理【查询架构树】的业务，参数为：{}", structureQueryPageDTO);

        //从DTO中取出产品id、架构id
        Integer productId = structureQueryPageDTO.getProductId();
        Integer structureId = structureQueryPageDTO.getStructureId();
        String structureName = structureQueryPageDTO.getStructureName();

        //查询出当前架构信息
        StructureVO structureVO;
        if (structureId == 0) {
            structureVO = new StructureVO();
        } else {
            structureVO = structureMapper.getStandardAndPictureById(structureId);
            List<StructurePictureVO> pictures = assoStructurePictureMapper.selectByStructureId(structureId);
            structureVO.setPictures(pictures);
        }

        //以下代码表示查询所有架构的路径和路径拼接成的架构名称并封装成map集合，从map集合中取值为后面给每一个架构的pathName中文路径赋值
        HashMap<String, String> map = getStructureIdAndStructureNameMap(productId);

        //以下代码表示若架构名称structureName有值则要根据架构名称模糊查询树，若架构名称structureName为null则表示查询了所有架构(不受影响)
        LambdaQueryWrapper<Structure> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Structure::getProductId, productId);
        if (structureName != null) {
            wrapper.like(Structure::getStructureName, structureName);
        }
        List<Structure> structures = this.list(wrapper);
        List<Integer> ids;
        if (structures != null && structures.size() > 0) {
            ids = structures.stream().map(Structure::getId).collect(Collectors.toList());
            List<String> paths = structures.stream().map(Structure::getPath).collect(Collectors.toList());
            paths.forEach(
                    item -> {
                        String[] pathStrs = item.split(",");
                        List<String> pathStr = new ArrayList<>(Arrays.asList(pathStrs));
                        pathStr.forEach(
                                tem -> {
                                    Integer paId = Integer.parseInt(tem);
                                    if (!ids.contains(paId)) {
                                        ids.add(paId);
                                    }
                                }
                        );
                    }
            );
        } else {
            ids = new ArrayList<>();
            ids.add(-1);
        }

        diGui(structureVO, map, structureId, productId, ids);
        return structureVO;
    }

    /**
     * 查询所有架构的路径和路径拼接成的架构名称
     *
     * @param queryPathStructureNameDTO 数据DTO对象
     * @return 返回查询到的数据
     */
    @Override
    public List<PathStructureNameVO> queryPathStructureName(QueryPathStructureNameDTO queryPathStructureNameDTO) {
        log.info("开始处理【查询所有架构路径和路径架构名称】的业务，参数为：{}", queryPathStructureNameDTO);

        Integer productId = queryPathStructureNameDTO.getProductId();
        Integer structureId = queryPathStructureNameDTO.getStructureId();

        ArrayList<PathStructureNameVO> pathStructureNames = new ArrayList<>();

        log.info("根据产品id查询所有架构数据");
        HashMap<String, String> map = getStructureIdAndStructureNameMap(productId);
        List<StructureVO> structures;
        //若有架构id则只查该架构底下的树，若没有架构id则查整棵产品的架构树
        if (structureId == null) {
            structures = structureMapper.selectAllByProductId(productId);
        } else {
            structures = structureMapper.selectAllByStructureId(structureId);
        }
        for (StructureVO structure : structures) {
            Integer id = structure.getId();
            String path = structure.getPath();
            String[] pathSplit = path.split(",");
            //String pathStructureName = "";
            StringBuilder builder = new StringBuilder();
            for (int i = 1; i < pathSplit.length; i++) {
                String structureName = map.get(pathSplit[i]);
                builder.append(structureName).append("/");
            }
            PathStructureNameVO pathStructureNameVO = new PathStructureNameVO()
                    .setId(id)
                    .setPath(path)
                    .setPathStructureName(builder + "");
            pathStructureNames.add(pathStructureNameVO);
        }

        return pathStructureNames;
    }

    /**
     * 删除架构
     *
     * @param id 架构id
     */
    @Override
    public void delete(Integer id) {
        log.info("开始处理【删除架构】的业务，参数为：{}", id);

        //检查尝试删除的数据是否存在
        Structure queryResult = structureMapper.getStandardById(id);
        if (queryResult == null) {
            ThrowException.throwXiaoShiException("删除架构失败，尝试访问的数据已不存在");
        }

        ArrayList<Integer> structureIds = new ArrayList<>();
        //根据架构id查询其包括其所有子级架构
        log.info("根据架构id查询其包括其所有子级架构");
        List<StructureVO> structures = structureMapper.selectByFindInSetPath(id + "");
        for (StructureVO structure : structures) {
            Integer structureId = structure.getId();
            structureIds.add(structureId);
        }

        //检查专题库表中是否有该架构数据，若有则不允许删除
        LambdaQueryWrapper<Project> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Project::getStructureId, structureIds);
        long count = projectService.count(wrapper);
        if (count > 0) {
            ThrowException.throwXiaoShiException("删除架构失败，该架构或其子级架构正在被专题库使用，无法删除");
        }

        //架构专利产品专题库标引表删除数据
        log.info("架构专利产品专题库标引表删除数据");
        assoStructurePatentMapper.deleteByStructureIds(structureIds);

        //检查产品架构专利号专题库表是否有该架构和其所有子级架构数据，若有则不允许删除
//        log.info("检查产品架构专利号专题库表是否有该架构和其所有子级架构数据，若有则不允许删除");
//        int count = assoStructurePatentMapper.countByStructureIds(structureIds);
//        if (count > 0) {
//            String message = "删除架构失败，该架构或其子级架构正在被专题库标引使用，请尝试先删除其关联数据";
//            log.info("{}", message);
//            throw new XiaoShiException(message);
//        }

        //架构和专利关联表删除数据
        //log.info("架构和专利关联表删除数据");
        //assoStructurePatentMapper.deleteByStructureIds(structureIds);

        //架构和图片关联表删除数据
        log.info("架构和图片关联表删除数据");
        assoStructurePictureMapper.deleteByStructureIds(structureIds);

        //架构表删除数据
        log.info("架构表删除数据");
        structureMapper.deleteByIds(structureIds);

        log.info("删除架构完成");

    }

    //递归组装架构树集合
    private void diGui(StructureVO structureVO, HashMap<String, String> map, Integer structureId, Integer productId, List<Integer> ids) {
//        List<StructureVO> structureVOs = structureMapper.selectByParentIdAndProductId(structureId, productId, ids);
        List<StructureVO> structureVOs = structureMapper.selectTree(structureId, productId, ids);
        if (structureVOs != null) {
            structureVO.setChildren(structureVOs);
            //给架构的路径名称pathName进行拼接赋值
            String path = structureVO.getPath();
            if (path != null && !path.equals("")) {
                String[] pathSplit = path.split(",");
                StringBuilder builder = new StringBuilder();
                for (int i = 1; i < pathSplit.length; i++) {
                    String structureName = map.get(pathSplit[i]);
                    builder.append(structureName).append("/");
                }
                structureVO.setPathName(builder + "");
            }
            for (StructureVO n : structureVOs) {
                structureId = n.getId();
                List<StructurePictureVO> pictures = assoStructurePictureMapper.selectByStructureId(structureId);
                n.setPictures(pictures);
                diGui(n, map, structureId, productId, ids);
            }
        }
    }

    /**
     * 获得某产品的架构树路径键值对 (map存储所有架构id(key)和架构名称(value)的键值对)
     *
     * @param productId 产品id
     * @return 返回map
     */
    private HashMap<String, String> getStructureIdAndStructureNameMap(Integer productId) {
        List<StructureVO> structures = structureMapper.selectAllByProductId(productId);
        //map存储所有架构id(key)和架构名称(value)的键值对
        HashMap<String, String> map = new HashMap<>();
        for (StructureVO structure : structures) {
            String structureStrId = structure.getId() + "";
            String structureName = structure.getStructureName();
            map.put(structureStrId, structureName);
        }
        return map;
    }

}
