package com.huashe.park.core.service.impl;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson2.JSONArray;
import com.huashe.common.exception.ServiceException;
import com.huashe.common.utils.DateUtils;
import com.huashe.common.utils.StringUtils;
import com.huashe.common.utils.uuid.Seq;
import com.huashe.park.common.EnhancedIDGenerator;
import com.huashe.park.common.consts.RedisKey;
import com.huashe.park.common.consts.enums.ConsStatus;
import com.huashe.park.common.excel.CsvAlias;
import com.huashe.park.common.i18n.MessageUtils;
import com.huashe.park.core.mapper.ConsUnitInfoMapper;
import com.huashe.park.core.mapper.PileMachineInfoMapper;
import com.huashe.park.core.service.IConsPileHoleInfoService;
import com.huashe.park.core.service.IConsUnitInfoService;
import com.huashe.park.core.service.IConsUnitMachineRelService;
import com.huashe.park.core.service.IMachineProcessResultService;
import com.huashe.park.core.service.IProjectInfoService;
import com.huashe.park.core.service.ISysCustConfigService;
import com.huashe.park.domain.dto.cons.CADJson;
import com.huashe.park.domain.dto.cons.ConsUnitMachine;
import com.huashe.park.domain.dto.cons.ResultCADCsv;
import com.huashe.park.domain.entity.ConsPileHoleInfo;
import com.huashe.park.domain.entity.ConsUnitInfo;
import com.huashe.park.domain.entity.ConsUnitMachineRel;
import com.huashe.park.domain.entity.CustSysConfig;
import com.huashe.park.domain.entity.PileMachineInfo;
import com.huashe.park.domain.entity.ProjectInfo;
import com.huashe.park.infrastructure.cfg.forest.CADForestCfg;
import com.huashe.park.infrastructure.excel.ExcelHandle;
import com.huashe.park.infrastructure.mqtt.CADService;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.file.FileUtils;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.text.csv.CsvWriteConfig;
import cn.hutool.core.text.csv.CsvWriter;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.http.HttpRequest;
import lombok.extern.slf4j.Slf4j;

/**
 * 施工单元Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-02-14
 */
@Service
@Slf4j
public class ConsUnitInfoServiceImpl implements IConsUnitInfoService {

    @Autowired
    private ConsUnitInfoMapper consUnitInfoMapper;

    @Autowired
    private IConsUnitMachineRelService consUnitMachineRelService;

    @Autowired
    private PileMachineInfoMapper pileMachineInfoService;

    @Autowired
    private IMachineProcessResultService machineProcessResultService;

    @Autowired
    private IConsPileHoleInfoService pileHoleInfoService;

    @Autowired
    private ExcelHandle excelHandle;

    @Autowired
    private CADService cadService;

    private final static String PROFILE = "/profile";

    @Autowired
    private EnhancedIDGenerator byte8Id;

    @Autowired
    private ISysCustConfigService sysCustConfigService;

    @Autowired
    private CADForestCfg cadForestCfg;

    private IProjectInfoService projectInfoService;

    @Autowired
    public void setProjectInfoService(@Lazy IProjectInfoService projectInfoService) {
        this.projectInfoService = projectInfoService;
    }

    /**
     * 查询施工单元
     * 
     * @param id 施工单元主键
     * @return 施工单元
     */
    @Override
    public ConsUnitMachine selectConsUnitInfoById(Long id) {
        ConsUnitInfo consUnitInfo = consUnitInfoMapper.selectConsUnitInfoById(id);
        List<Long> machineIds = consUnitMachineRelService.qryConsUnitMachineRelByUnitId(id);
        // constUnitInfo 赋值 ConsUnitMachine
        ConsUnitMachine result = new ConsUnitMachine();
        BeanUtils.copyProperties(consUnitInfo, result);
        result.setMachineId(machineIds);
        return result;
    }

    @Override
    public ConsUnitInfo selectConsUnitInfoByByteId(String byteId) {
        return consUnitInfoMapper.selectConsUnitInfoByByteId(byteId);
    }

    /**
     * 查询施工单元列表
     * 
     * @param consUnitInfo 施工单元
     * @return 施工单元
     */
    @Override
    public List<ConsUnitInfo> selectConsUnitInfoList(ConsUnitInfo consUnitInfo) {
        return consUnitInfoMapper.selectConsUnitInfoList(consUnitInfo);
    }

    /**
     * 新增施工单元
     * 
     * @param consUnitInfo 施工单元
     * @return 结果
     */

    @Transactional(rollbackFor = Exception.class)
    public int insertConsUnitInfo(ConsUnitMachine consUnitInfo) {
        // 处理施工单元信息
        ConsUnitInfo parentConsUnit = consUnitInfoMapper.selectConsUnitInfoById(consUnitInfo.getParentId());
        consUnitInfo.setAncestors(parentConsUnit.getAncestors() + "," + parentConsUnit.getId());
        consUnitInfo.setByteId(byte8Id.generate());
        consUnitInfo.setProjectId(parentConsUnit.getProjectId());
        int cnt = consUnitInfoMapper.insertConsUnitInfo(consUnitInfo);
        // 处理施工单元关联信息
        handleMachine(consUnitInfo);
        return cnt;
    }

    @Override
    public int insertConsUnitInfo(ConsUnitInfo consUnitInfo) {
        return consUnitInfoMapper.insertConsUnitInfo(consUnitInfo);
    }

    /**
     * 修改施工单元
     * 
     * @param consUnitInfo 施工单元
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateConsUnitInfo(ConsUnitMachine consUnitInfo) {
        if (consUnitInfo.getParentId().equals(consUnitInfo.getId())) {
            throw new ServiceException(MessageUtils.message("cons.unit.modify.not.self", consUnitInfo.getName()));
        }
        ConsUnitMachine oldConsUnit = selectConsUnitInfoById(consUnitInfo.getId());
        if (!consUnitInfo.getParentId().equals(oldConsUnit.getParentId())) {
            ConsUnitInfo newParentConsUnit = consUnitInfoMapper.selectConsUnitInfoById(consUnitInfo.getParentId());
            if (ObjectUtils.isNotEmpty(newParentConsUnit) && ObjectUtils.isNotEmpty(oldConsUnit)) {
                String newAncestors = newParentConsUnit.getAncestors() + "," + newParentConsUnit.getId();
                String oldAncestors = oldConsUnit.getAncestors();
                consUnitInfo.setAncestors(newAncestors);
                consUnitInfo.setProjectId(newParentConsUnit.getProjectId());
                updateConsUnitChildren(consUnitInfo.getId(), newParentConsUnit.getParentId(), newAncestors,
                    oldAncestors);
            }
        }
        return consUnitInfoMapper.updateConsUnitInfo(consUnitInfo);
    }

    @Override
    public void assignMachine(ConsUnitMachine consUnitInfo) {
        handleMachine(consUnitInfo);
    }

    /**
     * 批量删除施工单元
     * 
     * @param ids 需要删除的施工单元主键
     * @return 结果
     */
    @Override
    public int deleteConsUnitInfoByIds(Long[] ids) {
        return consUnitInfoMapper.deleteConsUnitInfoByIds(ids);
    }

    @Override
    public boolean hasChildById(Long id) {
        return consUnitInfoMapper.hasChildById(id) > 0;
    }

    /**
     * 删除施工单元信息
     * 
     * @param id 施工单元主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteConsUnitInfoById(Long id) {
        if (hasChildById(id)) {
            throw new ServiceException(MessageUtils.message("cons.unit.delete.no.child"));
        }
        pileHoleInfoService.deleteConsPileHoleInfoByConsUnitId(id);
        consUnitMachineRelService.deleteConsUnitMachineRelByUnitId(id);
        return consUnitInfoMapper.deleteConsUnitInfoById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteConsUnitInfoByProjectId(Long projId) {
        List<ConsUnitInfo> consUnitInfos = consUnitInfoMapper.selectConsUnitInfoList(new ConsUnitInfo() {
            {
                setProjectId(projId);
                setParentId(0L);
            }
        });
        if (CollectionUtils.isEmpty(consUnitInfos)) {
            return 0;
        }
        ConsUnitInfo consUnitInfo = consUnitInfos.get(0);
        return deleteConsUnitInfoById(consUnitInfo.getId());
    }

    @Override
    public void importConsUnitHole(ConsUnitInfo consUnitInfo) {
        int lastIndex = RuoYiConfig.getUploadPath().lastIndexOf("/");
        String filePath = consUnitInfo.getFileId().replaceFirst(PROFILE,
            RuoYiConfig.getUploadPath().substring(0, lastIndex));
        consUnitInfo.setFileId(filePath);
        consUnitInfoMapper.updateConsUnitInfo(consUnitInfo);
        // filePath 读取成 File
        File file = new File(filePath);

        CustSysConfig custSysConfig = sysCustConfigService.selectConfigObjByKey(cadForestCfg.getCadBizParam());
        if (ObjectUtils.isEmpty(custSysConfig)) {
            throw new ServiceException(MessageUtils.message("cons.biz.param.null"));
        }
        HttpRequest form = HttpRequest.post(cadForestCfg.getCadServer() + "/upload").form("dwg_file", file)
            .form("param", custSysConfig.getCfgJson());
        String json = form.execute().body();
        List<CADJson> cadJsons = JSONArray.parseArray(json, CADJson.class);
        pileHoleInfoService.deleteConsPileHoleInfoByConsUnitId(consUnitInfo.getId());
        List<ConsPileHoleInfo> consPileHoleInfo = new ArrayList<>(cadJsons.size());
        cadJsons.stream().filter(Objects::nonNull).forEach(item -> consPileHoleInfo.add(new ConsPileHoleInfo() {
            {
                setConsUnitId(consUnitInfo.getId());
                setDeltaX(item.getCoordX());
                setDeltaY(item.getCoordY());
                setLng(item.getEarthCoordX());
                setLat(item.getEarthCoordY());
                setHoleNum(item.getPileNumber());
                setConsStatus(ConsStatus.CONS_STATUS_01.getCode());
                setDesDept(40L);
            }
        }));
        // 将 cadJsons，分拆成 100个 List<ConsPileHoleInfo>，批量插入
        List<List<ConsPileHoleInfo>> data = ListUtil.partition(consPileHoleInfo, 100);
        for (List<ConsPileHoleInfo> datum : data) {
            pileHoleInfoService.importConsPileHole(datum);
        }
    }

    @Override
    public void exportConsUnitHole(ConsUnitInfo consUnitHole, HttpServletResponse response) {
        ConsUnitInfo consUnitInfo = consUnitInfoMapper.selectConsUnitInfoById(consUnitHole.getId());
        // filePath 读取成 File
        File file = new File(consUnitInfo.getFileId());
        File csvFile = generatePileHoleFile(consUnitInfo);
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        InputStream dwgFile = HttpRequest.post(cadForestCfg.getCadServer() + "/download").form("dwg_file", file)
            .form("csv_file", csvFile).execute().bodyStream();
        // 创建一个BufferedInputStream来缓冲输入流，提高读取效率
        try (BufferedInputStream bis = new BufferedInputStream(dwgFile);
            ServletOutputStream outputStream = response.getOutputStream();) {
            FileUtils.setAttachmentResponseHeader(response, file.getName());
            byte[] buffer = new byte[1024]; // 定义一个缓冲区
            int bytesRead;

            // 读取数据并写入到response的输出流中
            while ((bytesRead = bis.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            // 刷新并关闭流
            outputStream.flush();
        }
        catch (IOException e) {
            log.error("download input stream: {}", e.getMessage());
        }
    }

    @Override
    public void exportConsUnitRes(ConsUnitInfo consUnitHole, HttpServletResponse response) {
        ConsUnitInfo consUnitInfo = consUnitInfoMapper.selectConsUnitInfoById(consUnitHole.getId());
        File csvFile = generatePileHoleFile(consUnitInfo);
        writeFileToResponse(csvFile, response);
    }

    @Override
    public void upload(MultipartFile file) {
        HttpRequest form = HttpRequest.post(cadForestCfg.getCadServer() + "/upload").form("dwg_file", file);
        log.info(">>>>>>>>>>>>{}", form.toString());
        String json = form.execute().body();
        log.info(">>>>>>>>>>>>{}", json);
        cadService.newTopic(file);
    }

    private void writeFileToResponse(File file, HttpServletResponse response) {
        // 读取文件内容并写入响应输出流
        try (FileInputStream fis = new FileInputStream(file); OutputStream os = response.getOutputStream()) {
            FileUtils.setAttachmentResponseHeader(response, file.getName());
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            // 刷新并关闭流
            os.flush();
        }
        catch (IOException e) {
            log.error("Error during file download: {}", e.getMessage());
        }
    }

    @Override
    public List<ConsUnitInfo> machineAssignedUnit(Long machineId) {
        List<String> ancestors = consUnitInfoMapper.qryMachineAssignedUnitAncestors(machineId);
        // 使用 Java Stream API 将 ancestors 字符串按逗号分割，并去除重复项
        List<String> unitIds = ancestors.stream().flatMap(s -> Arrays.stream(s.split(","))).distinct()
            .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(unitIds)) {
            throw new ServiceException(MessageUtils.message("machine.not.assign.task"));
        }
        return consUnitInfoMapper.qryConsUnitInfoByIds(unitIds);
    }

    private void updateConsUnitChildren(Long id, Long projectId, String newAncestors, String oldAncestors) {
        List<ConsUnitInfo> children = consUnitInfoMapper.selectChildrenById(id);
        if (CollectionUtils.isEmpty(children)) {
            return;
        }
        for (ConsUnitInfo child : children) {
            child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
            child.setProjectId(projectId);
        }
        consUnitInfoMapper.updateConstUnitChildren(children);
    }

    private void handleMachine(ConsUnitMachine consUnitMachine) {
        consUnitMachineRelService.deleteConsUnitMachineRelByUnitId(consUnitMachine.getId());
        if (!CollectionUtils.isEmpty(consUnitMachine.getMachineId())) {
            List<ConsUnitMachineRel> consUnitMachineRel = new java.util.ArrayList<>();
            for (Long machineId : consUnitMachine.getMachineId()) {
                consUnitMachineRel.add(new ConsUnitMachineRel() {
                    {
                        setConsUnitId(consUnitMachine.getId());
                        setMachineId(machineId);
                        // 存在更新问题，在施工单元换节点的时候存在不一致的场景
                        // setUnitAncestors(consUnitMachine.getAncestors());
                    }
                });
            }
            consUnitMachineRelService.insertConsUnitMachineRelList(consUnitMachineRel);
        }
    }

    private File generatePileHoleFile(ConsUnitInfo consUnitInfo) {
        List<ProjectInfo> projectInfos = projectInfoService.selectProjectInfoList(new ProjectInfo());

        ProjectInfo projectInfo = projectInfos.get(0);
        CsvWriteConfig csvWriteConfig = new CsvWriteConfig();
        Field[] fields = ReflectUtil.getFields(ResultCADCsv.class);
        List<String> columnNames = new ArrayList<>();
        for (Field field : fields) {
            CsvAlias annotation = field.getAnnotation(CsvAlias.class);
            if (annotation != null) {
                columnNames.add(annotation.alias());
            }
        }
        for (int i = 0; i <= RedisKey.DEPTH_PRESS_TIPS.PILE_LENGTH_TIPS; i++) {
            columnNames.add(String.format("%.2fm", i * 0.25));
        }
        String filePath = StringUtils.format("{}/{}/{}_{}.{}", RuoYiConfig.getUploadPath(), DateUtils.datePath(),
            "cad-csv", Seq.getId("UPLOAD"), "csv");
        File csvFile = FileUtil.file(filePath);
        CsvWriter writer = CsvUtil.getWriter(csvFile, CharsetUtil.CHARSET_UTF_8, true, csvWriteConfig);

        List<String[]> header = new ArrayList<>();
        // 按行写出
        header.add(new String[] {
            "Project Name :", projectInfo.getName()
        });

        List<PileMachineInfo> pileMachineInfos = pileMachineInfoService.qryUnitMachine(consUnitInfo.getId());
        ConsUnitInfo consUnitInfo1 = consUnitInfoMapper.selectConsUnitInfoById(consUnitInfo.getId());
        if (!CollectionUtils.isEmpty(pileMachineInfos)) {
            List<String> machineNum = pileMachineInfos.stream().map(PileMachineInfo::getMachineNum)
                .collect(Collectors.toList());
            header.add(new String[] {
                "Machine Number :", String.join("|", machineNum)
            });
            List<String> equipment = pileMachineInfos.stream().map(PileMachineInfo::getEquipment)
                .collect(Collectors.toList());
            header.add(new String[] {
                "Equipment Number :", String.join("|", equipment)
            });
        }
        else {
            header.add(new String[] {
                "Machine Number :", ""
            });
            header.add(new String[] {
                "Equipment Number :", ""
            });
        }
        header.add(new String[] {
            "Cluster Number :", consUnitInfo1.getName()
        });
        header.add(new String[] {
            "Operator Name :", ""
        });

        writer.write(header);
        writer.write(new ArrayList<String[]>() {
            {
                add(Convert.toStrArray(columnNames));
            }
        });
        List<String[]> resultCADCsvs = machineProcessResultService.qeyConsUnitResult(consUnitInfo.getId());
        writer.write(resultCADCsvs);
        writer.flush();
        writer.close();
        return csvFile;
    }
}
