package org.dromara.elevator.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.elevator.constant.ElevatorConstant;
import org.dromara.elevator.domain.bo.EConfirmMessageBo;
import org.dromara.elevator.domain.bo.EElevatorUserBo;
import org.dromara.elevator.domain.vo.EElevatorUserVo;
import org.dromara.elevator.service.IEElevatorUserService;
import org.dromara.system.domain.SysDept;
import org.dromara.system.mapper.SysDeptMapper;
import org.springframework.stereotype.Service;
import org.dromara.elevator.domain.bo.EFaultMessageBo;
import org.dromara.elevator.domain.vo.EFaultMessageVo;
import org.dromara.elevator.domain.EFaultMessage;
import org.dromara.elevator.mapper.EFaultMessageMapper;
import org.dromara.elevator.service.IEFaultMessageService;

import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 故障消息;Service业务层处理
 *
 * @author zy
 * @date 2024-07-29
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class EFaultMessageServiceImpl implements IEFaultMessageService {

    private final EFaultMessageMapper baseMapper;
    private final SysDeptMapper deptMapper;
    private final DictService dictService;
    private final IEElevatorUserService elevatorUserService;

    /**
     * 查询故障消息;
     *
     * @param id 主键
     * @return 故障消息;
     */
    @Override
    public EFaultMessageVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询故障消息;列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 故障消息;分页列表
     */
    @Override
    public TableDataInfo<EFaultMessageVo> queryPageList(EFaultMessageBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<EFaultMessage> lqw = buildQueryWrapper(bo);
        if (bo.getMiniMsgFlag() && CollectionUtils.isEmpty(bo.getFollowElevatorList())) {
            return TableDataInfo.build(new ArrayList<>());
        }
        Page<EFaultMessageVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的故障消息;列表
     *
     * @param bo 查询条件
     * @return 故障消息;列表
     */
    @Override
    public List<EFaultMessageVo> queryList(EFaultMessageBo bo) {
        LambdaQueryWrapper<EFaultMessage> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<EFaultMessage> buildQueryWrapper(EFaultMessageBo bo) {
        List<Long> msgIds = null;
        List<String> followElevatorList;
        if (bo.getMiniMsgFlag()) {
            EElevatorUserBo elevatorUserBo = new EElevatorUserBo();
            elevatorUserBo.setUserId(String.valueOf(LoginHelper.getUserId()));
            elevatorUserBo.setIsConcern(ElevatorConstant.ON);
            List<EElevatorUserVo> elevatorUserList = elevatorUserService.queryList(elevatorUserBo);
            followElevatorList = StreamUtils.toList(elevatorUserList, EElevatorUserVo::getElevatorNum);
            if (CollectionUtils.isEmpty(followElevatorList)) {
                return Wrappers.lambdaQuery();
            }
            bo.setFollowElevatorList(followElevatorList);
            QueryWrapper<EFaultMessage> queryWrapper = new QueryWrapper<>();
            queryWrapper.select(" Max(id) AS id  ");
            queryWrapper.in("elevator_num", followElevatorList);
            queryWrapper.groupBy(" elevator_num ");
            queryWrapper.orderByDesc(" id ");
            queryWrapper.last(" limit 100 ");
            msgIds = this.baseMapper.selectList(queryWrapper).stream().map(EFaultMessage::getId).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(msgIds)) {
                bo.setFollowElevatorList(new ArrayList<>());
                return Wrappers.lambdaQuery();
            }
        }
        LambdaQueryWrapper<EFaultMessage> lqw = Wrappers.lambdaQuery();
        lqw.in(CollectionUtils.isNotEmpty(msgIds), EFaultMessage::getId, msgIds);
        lqw.eq(StringUtils.isNotBlank(bo.getFaultCode()), EFaultMessage::getFaultCode, bo.getFaultCode());
        lqw.like(StringUtils.isNotBlank(bo.getFaultName()), EFaultMessage::getFaultName, bo.getFaultName());
        lqw.eq(StringUtils.isNotBlank(bo.getDeviceCode()), EFaultMessage::getDeviceCode, bo.getDeviceCode());
        lqw.eq(StringUtils.isNotBlank(bo.getElevatorNum()), EFaultMessage::getElevatorNum, bo.getElevatorNum());
        lqw.like(StringUtils.isNotBlank(bo.getElevatorName()), EFaultMessage::getElevatorName, bo.getElevatorName());
        lqw.between(bo.getHappenTimeStart() != null && bo.getHappenTimeEnd() != null ,EFaultMessage::getHappenTime, bo.getHappenTimeStart(), bo.getHappenTimeEnd());
        lqw.eq(bo.getRecoverTime() != null, EFaultMessage::getRecoverTime, bo.getRecoverTime());
        lqw.eq(StringUtils.isNotBlank(bo.getSource()), EFaultMessage::getSource, bo.getSource());
        lqw.eq(StringUtils.isNotBlank(bo.getFloor()), EFaultMessage::getFloor, bo.getFloor());
        lqw.eq(StringUtils.isNotBlank(bo.getPicture()), EFaultMessage::getPicture, bo.getPicture());
        lqw.eq(bo.getState() != null, EFaultMessage::getState, bo.getState());
        lqw.eq(StringUtils.isNotBlank(bo.getAffirmPeople()), EFaultMessage::getAffirmPeople, bo.getAffirmPeople());
        lqw.eq(StringUtils.isNotBlank(bo.getCause()), EFaultMessage::getCause, bo.getCause());
        lqw.eq(StringUtils.isNotBlank(bo.getCauseAnalyse()), EFaultMessage::getCauseAnalyse, bo.getCauseAnalyse());
        lqw.eq(StringUtils.isNotBlank(bo.getExplain()), EFaultMessage::getExplain, bo.getExplain());
        lqw.eq(StringUtils.isNotBlank(bo.getVideoAddress()), EFaultMessage::getVideoAddress, bo.getVideoAddress());

        if (StringUtils.isNotBlank(bo.getFaultTypeKey())) {
            // 故障字典信息getFaultTypeKey
            Map<String, String> faultTypeMap = dictService.getAllDictByDictType(bo.getFaultTypeKey());
            lqw.in(MapUtils.isNotEmpty(faultTypeMap), EFaultMessage::getFaultCode, faultTypeMap.keySet());
        }

        lqw.and(ObjectUtil.isNotNull(bo.getDeptId()), w -> {
            List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
                .select(SysDept::getDeptId)
                .apply(DataBaseHelper.findInSet(bo.getDeptId(), "ancestors")));
            List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
            ids.add(bo.getDeptId());
            w.in(EFaultMessage::getDeptId, ids);
        }).orderByDesc(EFaultMessage::getCreateTime);
        return lqw;
    }

    /**
     * 新增故障消息;
     *
     * @param bo 故障消息;
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(EFaultMessageBo bo) {
        EFaultMessage add = MapstructUtils.convert(bo, EFaultMessage.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改故障消息;
     *
     * @param bo 故障消息;
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(EFaultMessageBo bo) {
        EFaultMessage update = MapstructUtils.convert(bo, EFaultMessage.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(EFaultMessage entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除故障消息;信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public List<EFaultMessageVo> queryFaultListByHappenTime(Date happenBeginTime, Date happenEndTime) {
        return baseMapper.selectVoList(Wrappers.<EFaultMessage>lambdaQuery()
            .between(EFaultMessage::getHappenTime, happenBeginTime, happenEndTime));
    }

    /**
     * 确认故障消息
     * @param bo
     * @return
     */
    @Override
    public boolean confirmMessage(EConfirmMessageBo bo) {
        EFaultMessage message = baseMapper.selectById(bo.getId());
        if (Objects.isNull(message)) {
            throw new ServiceException("暂未查询到此消息记录!");
        }
        message.setCause(bo.getCause());
        message.setCauseCode(bo.getCauseCode());
        message.setCauseAnalyse(bo.getCauseAnalyse());
        message.setCauseAnalyseCode(bo.getCauseAnalyseCode());
        message.setExplain(bo.getExplain());
        message.setAffirmPeople(bo.getAffirmPeople());
        message.setAttachment(bo.getAttachment());
        message.setState(1);
        message.setRecoverTime(new Date());

        return baseMapper.updateById(message) > 0;
    }

    /**
     * 图片导出
     * @param param
     * @param resp
     */
    @Override
    public void exportPicture(EFaultMessageBo param, HttpServletResponse resp) throws IOException {
        LambdaQueryWrapper<EFaultMessage> lqw = buildQueryWrapper(param);
        List<EFaultMessageVo> faultList = baseMapper.selectVoList(lqw);
        Workbook workbook = new XSSFWorkbook();
        resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        resp.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode("faultData", "UTF-8") + ".xlsx");
        ServletOutputStream out= resp.getOutputStream();
        try {
            Sheet sheet = workbook.createSheet("Product Report");
            // 创建标题行
            Row headerRow = sheet.createRow(0);
            headerRow.createCell(0).setCellValue("故障名称");
            headerRow.createCell(1).setCellValue("故障编号");
            headerRow.createCell(2).setCellValue("图片");
            // 数据信息
            int rowNum = 1;
            for (EFaultMessageVo message : faultList) {
                Row row = sheet.createRow(rowNum++);
                row.createCell(0).setCellValue(message.getFaultName());
                row.createCell(1).setCellValue(message.getFaultCode());
                sheet.setDefaultRowHeight((short) 800);
                sheet.setDefaultColumnWidth(20);
                String path = message.getPicture();
                if (StringUtils.isNotBlank(path)) {
                    for (int i = 0 ; i < 3 ; i++) {
                        // 下载照片到临时文件
                        String tempFileName = UUID.randomUUID() + ".jpg";
                        File tempFile = new File(tempFileName);
                        HttpUtil.downloadFile(path, tempFile);

                        // 插入图片
                        InputStream inputStream = new FileInputStream(tempFile);
                        byte[] imageBytes = IOUtils.toByteArray(inputStream);
                        int pictureIdx = workbook.addPicture(imageBytes, Workbook.PICTURE_TYPE_JPEG);
                        CreationHelper helper = workbook.getCreationHelper();
                        Drawing<?> drawing = sheet.createDrawingPatriarch();
                        ClientAnchor anchor = helper.createClientAnchor();

                        anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_DONT_RESIZE);
                        anchor.setCol1(2 + i);
                        anchor.setRow1(rowNum - 1);
                        // 图片结束列
                        anchor.setCol2(3 + i);
                        // 图片结束行
                        anchor.setRow2(rowNum);
                        Picture picture = drawing.createPicture(anchor, pictureIdx);
                        picture.resize(1,1);
                        inputStream.close();
                        tempFile.delete();
                    }
                }
            }
            workbook.write(out);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            workbook.close();
            out.close();
        }

        log.info("数据和图片已成功导出到Excel文件中！");
    }
}
