package cn.com.ty.lift.business.emergency.controller;


import cn.com.ty.lift.business.emergency.dto.*;
import cn.com.ty.lift.business.emergency.entity.EmergencyRepair;
import cn.com.ty.lift.business.emergency.entity.ErRecordCost;
import cn.com.ty.lift.business.emergency.entity.ErRecordImg;
import cn.com.ty.lift.business.emergency.entity.LiftFault;
import cn.com.ty.lift.business.emergency.service.*;
import cn.com.ty.lift.business.evaluation.dao.entity.Evaluation;
import cn.com.ty.lift.business.evaluation.service.EvaluationService;
import cn.com.ty.lift.business.library.dao.entity.PlatformCompanyLiftRelevance;
import cn.com.ty.lift.business.library.service.PlatformCompanyLiftRelevanceService;
import cn.com.ty.lift.business.project.dao.entity.Region;
import cn.com.ty.lift.business.push.service.PushUserService;
import cn.com.ty.lift.common.constants.ApiConstants;
import cn.com.ty.lift.common.constants.CommonEnum;
import cn.com.ty.lift.common.constants.WebSocketConstants;
import cn.com.ty.lift.common.model.CountPage;
import cn.com.ty.lift.common.model.PushMessage;
import cn.com.ty.lift.common.model.PushUserInfo;
import cn.com.ty.lift.common.utils.DateUtils;
import cn.com.ty.lift.common.utils.SendMessageUtil;
import cn.com.ty.lift.common.utils.ValuePool;
import cn.com.ty.lift.common.utils.WordUtil;
import cn.com.ty.lift.common.verify.Val;
import cn.com.ty.lift.common.verify.Validate;
import cn.com.ty.lift.common.verify.Validation;
import cn.com.xwy.boot.web.dto.RestResponse;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 临修记录 前端控制器
 * </p>
 *
 * @author wcz
 * @since 2019-12-04
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/emergency/emergency-repair")
public class EmergencyRepairController {

    private EmergencyRepairService              emergencyRepairService;
    private ErRecordCostService                 erRecordCostService;
    private ErRecordImgService                  erRecordImgService;
    private LiftFaultService                    liftFaultService;
    private EvaluationService                   evaluationService;
    private PlatformCompanyLiftRelevanceService platformCompanyLiftRelevanceService;
    private EmergencyHistoryService             emergencyHistoryService;
    private JmsMessagingTemplate                jmsMessagingTemplate;
    private PushUserService                     pushUserService;

    private static final Map<String, String> headerAlias = new LinkedHashMap<>();

    static {
        headerAlias.put("areaName", "区域");
        headerAlias.put("projectName", "项目");
        headerAlias.put("registrationCode", "注册代码");
        headerAlias.put("devicePosition", "电梯位置");
        headerAlias.put("workerName", "维修工");
        headerAlias.put("sourceDesc", "故障来源");
        headerAlias.put("repairReasonDesc", "召修原因");
        headerAlias.put("callerDate", "报修时间");
        headerAlias.put("arriveTime", "到达时间");
        headerAlias.put("stopDate", "停梯时间");
        headerAlias.put("recoveryDate", "恢复时间");
        headerAlias.put("repairDuration", "急修时长");
    }

    /**
     * 根据id查找
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("findOne")
    @Validation(fields = {"id"})
    public RestResponse findOne(@Val @RequestBody RepairRequest request) {
        Long id = request.getId();
        RepairResponse result = emergencyRepairService.infoById(request.getId());
        if (null != result) {
            //急修费用
            result.setErRecordCosts(erRecordCostService.listByErRecordId(id));
            //急修图片
            result.setErRecordImgs(erRecordImgService.listByErRecordId(id));
            //评价
            Evaluation byRecord = evaluationService.findByRecord(id, ValuePool.EVALUATE_SOURCE_EMERGENCY);
            if (Objects.nonNull(byRecord)) result.setEvaluation(byRecord);
            //故障信息
            fillFault(result);
        }
        return RestResponse.success(result);
    }

    /**
     * 解析故障项id字符串，查询出相关的故障项
     *
     * @param result RepairResponse
     */
    private void fillFault(RepairResponse result) {
        List<Long> partIds = splitIds(result.getFaultPart());
        List<Long> reasonIds = splitIds(result.getFaultReason());
        List<Long> natureIds = splitIds(result.getFaultNature());
        List<Long> handleIds = splitIds(result.getFaultHandle());
        List<Long> dutyIds = splitIds(result.getFaultDuty());

        Set<Long> faultIds = new HashSet<>();
        faultIds.addAll(partIds);
        faultIds.addAll(reasonIds);
        faultIds.addAll(natureIds);
        faultIds.addAll(handleIds);
        faultIds.addAll(dutyIds);
        if (faultIds.isEmpty()) return;
        Collection<LiftFault> liftFaults = liftFaultService.listByIds(faultIds);
        if (IterUtil.isEmpty(liftFaults)) return;
        result.setFaultParts(liftFaults.stream().filter(fault -> (partIds.contains(fault.getId()))).collect(Collectors.toList()));
        result.setFaultReasons(liftFaults.stream().filter(fault -> (reasonIds.contains(fault.getId()))).collect(Collectors.toList()));
        result.setFaultNatures(liftFaults.stream().filter(fault -> (natureIds.contains(fault.getId()))).collect(Collectors.toList()));
        result.setFaultHandles(liftFaults.stream().filter(fault -> (handleIds.contains(fault.getId()))).collect(Collectors.toList()));
        result.setFaultDuties(liftFaults.stream().filter(fault -> (dutyIds.contains(fault.getId()))).collect(Collectors.toList()));
    }

    /**
     * 拆分故障项id字符串，组装成List<Long>
     *
     * @param idStr ids
     * @return List<Long>
     */
    private List<Long> splitIds(String idStr) {
        if (StrUtil.isEmpty(idStr)) {
            return Collections.emptyList();
        }
        return Arrays.stream(idStr.split(ValuePool.COMMA)).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
    }

    /**
     * 新增急修
     *
     * @param entity EmergencyRepair
     * @return RestResponse
     */
    @PostMapping("add")
    public RestResponse add(@Valid @RequestBody EmergencyRepair entity) {
        Long mtCompanyId = entity.getMtCompanyId();
        Long liftId = entity.getLiftId();
        int repairing = emergencyRepairService.countRepairing(mtCompanyId, liftId);
        Validate.notTrue(repairing > 0, ValuePool.EMERGENCY_HAVE_REPAIRING);
        PlatformCompanyLiftRelevance relevance = platformCompanyLiftRelevanceService.findByMtCompanyAndLift(entity.getMtCompanyId(), entity.getLiftId());
        if (Objects.nonNull(relevance)) {
            Validate.notTrue(CommonEnum.LiftStatus.ANNUAL_INSPECTION.getCode().equals(relevance.getLiftCompanyStatus()), ValuePool.EMERGENCY_LIFT_IS_IN_INSPECTION);
        }

        //派单时间 急修单创建时间
        entity.setHasEvaluate(0);
        entity.setAssignTime(LocalDateTime.now());
        entity.setStatus(ValuePool.EMERGENCY_STATE_TO_DO);
        boolean result = emergencyRepairService.save(entity);
        if (result) {
            //消息推送
            Long id = entity.getId();
            List<Long> userIds = new ArrayList<>();
            userIds.add(entity.getWorkerId1());
            Region region = pushUserService.findRegionByEmergencyRepair(id, mtCompanyId);
            if (Objects.nonNull(region)) {
                userIds.add(region.getUserId());
            }
            List<PushUserInfo> director = pushUserService.listHighDirector(mtCompanyId);
            List<PushUserInfo> pushUserInfos = new ArrayList<>(director);
            if(!userIds.isEmpty()){
                List<PushUserInfo> userInfos = pushUserService.listByUserIds(mtCompanyId, userIds);
                pushUserInfos.addAll(userInfos);
            }
            if(!pushUserInfos.isEmpty()){
                RepairResponse info = emergencyRepairService.infoById(id);
                PushMessage pushMessage = PushMessage.emergencyAssign(info.getAreaName(), info.getProjectName(), info.getDevicePosition(),
                        info.getRegistrationCode(), info.getCallerDate(), info.getIsTrapped(),
                        info.getCallerFaultDescription(), info.getWorkerName());
                if(ApiConstants.ACCESS_TYPE_PUBLIC == entity.getMobileType()) {
                    pushMessage = PushMessage.emergencyAssignByProperty(info.getAreaName(), info.getProjectName(), info.getDevicePosition(),
                            info.getRegistrationCode(), info.getCallerDate(), info.getIsTrapped(),
                            info.getCallerFaultDescription(), info.getWorkerName());
                    //物业端发起的急修要将消息推送到pc端
                    List<Long> pushUserIds = pushUserInfos.stream().map(PushUserInfo::getUserId).collect(Collectors.toList());
                    SendMessageUtil.sendMessageToPC(jmsMessagingTemplate, pushUserIds, pushMessage.getContent(),
                            WebSocketConstants.MessageType.EMERGENCY_MSG);
                }
                pushMessage.sendTokenOnPlatform(jmsMessagingTemplate, pushUserInfos);
            }
        }
        return RestResponse.success(result);
    }

    /**
     * 分页查询急修中的 status = 0, 1
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("list")
    @Validation(fields = "mtCompanyId")
    public RestResponse list(@Val @RequestBody RepairRequest request) {
        //按状态排序，待修理--修理中；同一状态下按等待时间最长的放在最上面，加一列等待时间列表
        request.addAsc("status");
        IPage<RepairResponse> pages = emergencyRepairService.pageByRepairing(request);
        long count = pages.getTotal();
        return RestResponse.success(CountPage.getCountPage(count, pages));
    }

    private RestResponse pageByCondition(RepairRequest request) {
        //不做查询时间区间检查
//        LocalDate repairBegin = request.getRepairBegin();
//        LocalDate repairEnd = request.getRepairEnd();
//        if (Objects.nonNull(repairBegin) && Objects.nonNull(repairEnd)) {
//            long until = repairEnd.until(repairBegin, ChronoUnit.DAYS);
//            Validate.notTrue(until > DateUtils.daysOfYear(), "最多只允许查询间隔" + DateUtils.daysOfYear() + "天的记录");
//        } else if (Objects.nonNull(repairBegin) && Objects.isNull(repairEnd)) {
//            request.setRepairEnd(repairBegin.plusDays(DateUtils.daysOfYear()));
//        } else if (Objects.isNull(repairBegin) && Objects.nonNull(repairEnd)) {
//            request.setRepairBegin(repairEnd.plusDays(0 - DateUtils.daysOfYear()));
//        } else {
//            request.setRepairBegin(LocalDate.now().plusDays(0 - DateUtils.daysOfYear()));
//            request.setRepairEnd(LocalDate.now());
//        }
        LocalDate repairBegin = request.getRepairBegin();
        LocalDate repairEnd = request.getRepairEnd();
        if(Objects.nonNull(repairBegin) && Objects.nonNull(repairEnd)){
            Validate.isTrue(repairBegin.isBefore(repairEnd), ValuePool.beginTimeToEndTimeIllegal(repairBegin, repairEnd));
        }
        IPage<RepairResponse> pages = emergencyRepairService.pageByCondition(request);
        long count = emergencyRepairService.countByCondition(request);
        return RestResponse.success(CountPage.getCountPage(count, pages));
    }

    /**
     * 根据条件分页查询
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("page")
    @Validation(fields = {"status", "mtCompanyId"})
    public RestResponse page(@Val @RequestBody RepairRequest request) {
        //已完成: 按恢复时间由近及远排序, 已关闭: 按报修时间由近及远排序
        if (Objects.equals(request.getStatus(), ValuePool.EMERGENCY_STATE_COMPLETE)) {
            request.addDesc("recovery_date");
        } else {
            request.addDesc("caller_date");
        }
        return pageByCondition(request);
    }

    /**
     * 急修收款中急修列表，不检查status
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("pageCost")
    @Validation(fields = {"mtCompanyId"})
    public RestResponse pageCost(@Val @RequestBody RepairRequest request) {
        //急修收款: 按结束时间由近及远排序
        request.addDesc("update_date");
        return pageByCondition(request);
    }

    /**
     * 根据条件分页查询
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("pageWorker")
    @Validation(fields = {"mtCompanyId", "workerId"})
    public RestResponse pageWorker(@Val @RequestBody RepairRequest request) {
        if (Objects.equals(ValuePool.EMERGENCY_STATE_COMPLETE, request.getStatus())) {
            request.addDesc("recovery_date");
        }else{
            request.addDesc("assign_time");
        }
        IPage<RepairResponse> pages = emergencyRepairService.pageByCondition(request);
        return RestResponse.success(pages);
    }

    /**
     * app急修数量统计
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("count")
    @Validation(fields = {"mtCompanyId", "userId"})
    public RestResponse count(@Val @RequestBody RepairRequest request) {
        EmergencyCount count = emergencyRepairService.count(request);
        return RestResponse.success(count);
    }

    /**
     * 急修转派
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("transfer")
    @Validation(fields = {"id", "workerId"})
    public RestResponse transfer(@Val @RequestBody RepairRequest request) {
        EmergencyRepair entity = emergencyRepairService.getById(request.getId());
        Validate.notNull(entity, ValuePool.EMERGENCY_NOT_EXIST);

        //如果已经接单，在急修中了不能被转派
        Validate.notEquals(entity.getStatus(), ValuePool.EMERGENCY_STATE_DOING, ValuePool.EMERGENCY_HAD_TAKEN);
        Long originId = entity.getWorkerId1();
        //本急修单的负责维保工
        entity.setWorkerId1(request.getWorkerId());

        boolean result = emergencyRepairService.saveOrUpdate(entity);
        if (result) {
            //消息推送
            Long id = entity.getId();
            Long mtCompanyId = entity.getMtCompanyId();
            List<Long> userIds = new ArrayList<>();
            userIds.add(request.getWorkerId());
            Region region = pushUserService.findRegionByEmergencyRepair(id, mtCompanyId);
            if (Objects.nonNull(region)) {
                userIds.add(region.getUserId());
            }
            List<PushUserInfo> director = pushUserService.listHighDirector(mtCompanyId);
            List<PushUserInfo> pushUserInfos = new ArrayList<>(director);
            if(!userIds.isEmpty()){
                List<PushUserInfo> userInfos = pushUserService.listByUserIds(mtCompanyId, userIds);
                pushUserInfos.addAll(userInfos);
            }
            RepairResponse info = emergencyRepairService.infoById(id);
            if(!pushUserInfos.isEmpty()){
                PushMessage.emergencyTransfer(info.getAreaName(), info.getProjectName(), info.getDevicePosition(), info.getRegistrationCode(), info.getCallerDate(), info.getIsTrapped(), info.getCallerFaultDescription(), info.getWorkerName()).sendTokenOnPlatform(jmsMessagingTemplate, pushUserInfos);
            }
            //【原被派单人】收到消息：
            if(Objects.nonNull(originId)){
                PushUserInfo originWorker = pushUserService.listByUserId(mtCompanyId, originId);
                PushMessage.emergencyTransferForOriginal(info.getAreaName(), info.getProjectName(), info.getDevicePosition(), info.getRegistrationCode(), info.getWorkerName()).sendTokenOnPlatform(jmsMessagingTemplate, originWorker);
            }
        }
        return RestResponse.success(result);
    }

    /**
     * 关闭急修
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("close")
    @Validation(fields = {"id", "closeReason"})
    public RestResponse close(@Val @RequestBody RepairRequest request) {
        EmergencyRepair entity = emergencyRepairService.getById(request.getId());
        Validate.notNull(entity, ValuePool.EMERGENCY_NOT_EXIST);

        entity.setCloseReason(request.getCloseReason());
        //新状态（-1 暂停中，0 待修理；1 修理中；2 已完成 3 已关闭）
        entity.setStatus(ValuePool.EMERGENCY_STATE_CLOSE);

        return emergencyRepairService.close(entity);
    }

    /**
     * app-其他角色获取自己关联的项目的急修任务
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("pageByUser")
    @Validation(fields = {"userId"})
    public RestResponse pageByUser(@Val @RequestBody RepairRequest request) {
        if (Objects.equals(ValuePool.EMERGENCY_STATE_COMPLETE, request.getStatus())) {
            request.addDesc("recovery_date");
        }else{
            request.addDesc("assign_time");
        }
        IPage<RepairResponse> pages = emergencyRepairService.pageByUser(request);
        return RestResponse.success(pages);
    }

    /**
     * app-点击接单，保存接单时间到急修表的接单时间接口
     * 接单的时候就是签到
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("taking")
    @Validation(fields = {"id", "takingTime"})
    public RestResponse taking(@Val @RequestBody RepairRequest request) {
        EmergencyRepair entity = emergencyRepairService.getById(request.getId());
        Validate.notNull(entity, ValuePool.EMERGENCY_NOT_EXIST);

        Validate.equals(entity.getStatus(), ValuePool.EMERGENCY_STATE_TO_DO, ValuePool.EMERGENCY_MUST_TO_DO);
        LocalDateTime assignTime = entity.getAssignTime();
        LocalDateTime takingTime = request.getTakingTime();
        if(Objects.nonNull(assignTime)){
            Validate.isTrue(assignTime.isBefore(takingTime), ValuePool.emergencyProcessTimeIllegal("派单时间", assignTime));
        }
        entity.setTakingTime(takingTime);
        //todo:修改电梯的业务状态为急修中,如果存在维保中，要修改
        entity.setStatus(ValuePool.EMERGENCY_STATE_DOING);
        RestResponse result = emergencyRepairService.taking(entity);
        if (Objects.equals("1", result.getStatusCode())) {
            Long id = entity.getId();
            Long mtCompanyId = entity.getMtCompanyId();
            Region region = pushUserService.findRegionByEmergencyRepair(id, mtCompanyId);
            List<Long> userIds = new ArrayList<>();
            if (Objects.nonNull(region)) {
                userIds.addAll(region.members());
            }
            List<PushUserInfo> director = pushUserService.listHighDirector(mtCompanyId);
            List<PushUserInfo> pushUserInfos = new ArrayList<>(director);
            if(!userIds.isEmpty()){
                List<PushUserInfo> userInfos = pushUserService.listByUserIds(mtCompanyId, userIds);
                pushUserInfos.addAll(userInfos);
            }
            if(!pushUserInfos.isEmpty()){
                RepairResponse info = emergencyRepairService.infoById(id);
                PushMessage pushMessage = PushMessage.emergencyTaking(info.getAreaName(), info.getProjectName(),
                        info.getDevicePosition(), info.getRegistrationCode(), info.getCallerDate());
                pushMessage.sendTokenOnPlatform(jmsMessagingTemplate, pushUserInfos);
                //推送消息到pc端
                List<Long> pushUserIds = pushUserInfos.stream().map(PushUserInfo::getUserId).collect(Collectors.toList());
                SendMessageUtil.sendMessageToPC(jmsMessagingTemplate, pushUserIds, pushMessage.getContent(),
                        WebSocketConstants.MessageType.EMERGENCY_MSG);
            }
        }
        return result;
    }

    /**
     * app-点击到达，保存到达时间到急修表的到达时间中
     *
     * @param request 请求参数
     * @return RestResponse
     */
    @PostMapping("arrive")
    @Validation(fields = {"id", "arriveTime", "position"})
    public RestResponse arrive(@Val @RequestBody RepairRequest request) {
        EmergencyRepair entity = emergencyRepairService.getById(request.getId());
        Validate.notNull(entity, ValuePool.EMERGENCY_NOT_EXIST);

        Validate.equals(entity.getStatus(), ValuePool.EMERGENCY_STATE_DOING, ValuePool.EMERGENCY_MUST_IN_DOING);
        LocalDateTime takingTime = entity.getTakingTime();
        LocalDateTime arriveTime = request.getArriveTime();
        if(Objects.nonNull(takingTime)){
            Validate.isTrue(takingTime.isBefore(arriveTime), ValuePool.emergencyProcessTimeIllegal("接单时间", takingTime));
        }
        entity.setArriveTime(arriveTime);
        entity.setPosition(request.getPosition());

        boolean result = emergencyRepairService.updateById(entity);
        if (result) {
            Long id = entity.getId();
            Long mtCompanyId = entity.getMtCompanyId();
            Region region = pushUserService.findRegionByEmergencyRepair(id, mtCompanyId);
            List<Long> userIds = new ArrayList<>();
            if (Objects.nonNull(region)) {
                userIds.addAll(region.members());
            }
            List<PushUserInfo> director = pushUserService.listHighDirector(mtCompanyId);
            List<PushUserInfo> pushUserInfos = new ArrayList<>(director);
            if(!userIds.isEmpty()){
                List<PushUserInfo> userInfos = pushUserService.listByUserIds(mtCompanyId, userIds);
                pushUserInfos.addAll(userInfos);
            }
            if(!pushUserInfos.isEmpty()){
                RepairResponse info = emergencyRepairService.infoById(id);
                PushMessage pushMessage = PushMessage.emergencyArrive(info.getAreaName(), info.getProjectName(),
                        info.getDevicePosition(), info.getRegistrationCode(), info.getCallerDate(), info.getArriveTime());
                pushMessage.sendTokenOnPlatform(jmsMessagingTemplate, pushUserInfos);
                //推送消息到pc端
                List<Long> pushUserIds = pushUserInfos.stream().map(PushUserInfo::getUserId).collect(Collectors.toList());
                SendMessageUtil.sendMessageToPC(jmsMessagingTemplate, pushUserIds, pushMessage.getContent(),
                        WebSocketConstants.MessageType.EMERGENCY_MSG);
            }
        }
        return RestResponse.success(result);
    }

    /**
     * app-停梯时间+安全确认，保存停梯时间+安全确认项到急修表中
     *
     * @param request 请求参数
     * @return RestResponse
     */
    @PostMapping("stop")
    @Validation(fields = {"id", "stopDate", "safetyConfirm"})
    public RestResponse stopLift(@Val @RequestBody RepairRequest request) {
        EmergencyRepair entity = emergencyRepairService.getById(request.getId());
        Validate.notNull(entity, ValuePool.EMERGENCY_NOT_EXIST);

        Validate.equals(entity.getStatus(), ValuePool.EMERGENCY_STATE_DOING, ValuePool.EMERGENCY_MUST_IN_DOING);
        LocalDateTime arriveTime = entity.getArriveTime();
        LocalDateTime stopDate = request.getStopDate();
        if(Objects.nonNull(arriveTime)){
            Validate.isTrue(arriveTime.isBefore(stopDate), ValuePool.emergencyProcessTimeIllegal("到达时间", arriveTime));
        }
        entity.setStopDate(stopDate);
        entity.setSafetyConfirm(request.getSafetyConfirm());

        boolean result = emergencyRepairService.updateById(entity);
        return RestResponse.success(result);
    }

    /**
     * 新增急修收费单项
     *
     * @param request 请求参数
     * @return RestResponse
     */
    @PostMapping("addCost")
    @Validation(fields = {"id", "erRecordCosts"})
    public RestResponse addCost(@Val @RequestBody RepairRequest request) {
        EmergencyRepair repair = emergencyRepairService.getById(request.getId());
        Validate.notNull(repair, ValuePool.EMERGENCY_NOT_EXIST);

        List<ErRecordCost> costs = erRecordCostService.listByErRecordId(repair.getId());
        List<ErRecordCost> erRecordCosts = request.getErRecordCosts();
        if(Objects.isNull(costs)){
            costs = new ArrayList<>();
        }
        costs.addAll(erRecordCosts);
        BigDecimal costTotal = costs.stream().filter(cost -> Objects.nonNull(cost.getCostMoney())).map(ErRecordCost::getCostMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        repair.setCostTotal(costTotal);
        BigDecimal payTotal = costs.stream().filter(cost -> Objects.nonNull(cost.getPayMoney())).map(ErRecordCost::getPayMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        repair.setPayTotal(payTotal);
        BigDecimal invoiceTotal = costs.stream().filter(cost -> Objects.nonNull(cost.getInvoiceMoney())).map(ErRecordCost::getInvoiceMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        repair.setInvoiceTotal(invoiceTotal);

        return emergencyRepairService.addCost(repair, erRecordCosts);
    }

    /**
     * 删除急修下的收费项
     *
     * @param request 请求参数
     * @return RestResponse
     */
    @PostMapping("deleteCost")
    @Validation(fields = {"id", "erCostItemId"})
    public RestResponse deleteCost(@Val @RequestBody RepairRequest request) {
        Long id = request.getId();
        Long erCostItemId = request.getErCostItemId();

        EmergencyRepair repair = emergencyRepairService.getById(id);
        Validate.notNull(repair, ValuePool.EMERGENCY_NOT_EXIST);

        ErRecordCost erRecordCost = erRecordCostService.findByRecordAndId(id, erCostItemId);
        Validate.notNull(erRecordCost, ValuePool.EMERGENCY_COST_NOT_EXIST);

        BigDecimal costTotal = repair.getCostTotal();
        BigDecimal costMoney = erRecordCost.getCostMoney();
        //收费项的金额不能大于急修单的总金额
        if (Objects.nonNull(costTotal) && Objects.nonNull(costMoney)) {
            Validate.notTrue(costTotal.compareTo(costMoney) < 0, ValuePool.EMERGENCY_COST_INCORRECT_AMOUNT);
            repair.setCostTotal(costTotal.subtract(costMoney));
        }

        BigDecimal payTotal = repair.getPayTotal();
        BigDecimal payMoney = erRecordCost.getPayMoney();
        if (Objects.nonNull(payTotal) && Objects.nonNull(payMoney)) {
            Validate.notTrue(payTotal.compareTo(payMoney) < 0, ValuePool.EMERGENCY_COST_INCORRECT_AMOUNT);
            repair.setPayTotal(payTotal.subtract(payMoney));
        }

        BigDecimal invoiceTotal = repair.getInvoiceTotal();
        BigDecimal invoiceMoney = erRecordCost.getInvoiceMoney();
        if (Objects.nonNull(invoiceTotal) && Objects.nonNull(invoiceMoney)) {
            Validate.notTrue(invoiceTotal.compareTo(invoiceMoney) < 0, ValuePool.EMERGENCY_COST_INCORRECT_AMOUNT);
            repair.setInvoiceTotal(invoiceTotal.subtract(invoiceMoney));
        }

        return emergencyRepairService.deleteCost(repair, erRecordCost);
    }

    /**
     * 修改急修单下的急修收费项
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("modifyCost")
    @Validation(fields = {"id", "erRecordCosts"})
    public RestResponse modifyCost(@Val @RequestBody RepairRequest request) {
        Long recordId = request.getId();
        EmergencyRepair repair = emergencyRepairService.getById(recordId);
        Validate.notNull(repair, ValuePool.EMERGENCY_NOT_EXIST);

        List<ErRecordCost> erRecordCosts = request.getErRecordCosts();
        BigDecimal costTotal = erRecordCosts.stream().filter(cost -> Objects.nonNull(cost.getCostMoney())).map(ErRecordCost::getCostMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        repair.setCostTotal(costTotal);
        BigDecimal payTotal = erRecordCosts.stream().filter(cost -> Objects.nonNull(cost.getPayMoney())).map(ErRecordCost::getPayMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        repair.setPayTotal(payTotal);
        BigDecimal invoiceTotal = erRecordCosts.stream().filter(cost -> Objects.nonNull(cost.getInvoiceMoney())).map(ErRecordCost::getInvoiceMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        repair.setInvoiceTotal(invoiceTotal);

        List<ErRecordCost> oldRecordCosts = erRecordCostService.listByErRecordId(recordId);

        return emergencyRepairService.modifyCost(repair, oldRecordCosts, erRecordCosts);
    }

    /**
     * 根据电梯类型查询故障信息（集合结构）
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("fault/list")
    @Validation(fields = {"liftCategory"})
    public RestResponse faultList(@Val @RequestBody RepairRequest request) {
        List<LiftFault> lists = liftFaultService.listByLiftCategory(request.getLiftCategory());
        return RestResponse.success(lists);
    }

    /**
     * 根据电梯类型获取急修故障信息列表(树结构)
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("fault/tree")
    @Validation(fields = {"liftCategory"})
    public RestResponse faultTree(@Val @RequestBody RepairRequest request) {
        List<LiftFault> liftFaultList = liftFaultService.listByLiftCategory(request.getLiftCategory());
        Validate.notNull(liftFaultList, ValuePool.EMERGENCY_FAULT_MISSING);
        //根据故障列表组装tree
        //先按照faultCategory，groupby,再按照faultType，groupby
        Map<String, Map<String, List<LiftFault>>> categories = liftFaultList.stream().collect(Collectors.groupingBy(LiftFault::getFaultCategory, Collectors.groupingBy(LiftFault::getFaultType)));
        List<LiftFaultCategory> liftFaultCategories = new ArrayList<>();
        if (MapUtil.isNotEmpty(categories)) {
            categories.forEach((category, faultTypes) -> {
                List<LiftFaultType> liftFaultTypes = new ArrayList<>();
                faultTypes.forEach((type, liftFaults) -> {
                    liftFaultTypes.add(new LiftFaultType(type, liftFaults));
                });
                liftFaultCategories.add(new LiftFaultCategory(category, liftFaultTypes));
            });
        }
        return RestResponse.success(liftFaultCategories);
    }

    /**
     * 急修评价
     *
     * @param entity 封装评价的参数model
     * @return RestResponse
     */
    @PostMapping("evaluate")
    public RestResponse evaluate(@Valid @RequestBody Evaluation entity) {
        Long recordId = entity.getRecordId();
        EmergencyRepair repair = emergencyRepairService.getById(recordId);
        Validate.notNull(repair, ValuePool.EMERGENCY_NOT_EXIST);

        Validate.equals(repair.getStatus(), ValuePool.EMERGENCY_STATE_COMPLETE, ValuePool.EMERGENCY_MUST_COMPLETE);
        //1. 根据急修中的评价标识判断
        Validate.notEquals(1, repair.getHasEvaluate(), ValuePool.EMERGENCY_HAD_EVALUATE);
        //2. 查询急修评价记录判断
        int has = evaluationService.countByRecord(entity.getRecordId(), ValuePool.EVALUATE_SOURCE_EMERGENCY);
        Validate.notTrue(has > 0, ValuePool.EMERGENCY_HAD_EVALUATE);
        //来源（1 维保；2 急修）
        entity.setSource(ValuePool.EVALUATE_SOURCE_EMERGENCY);
        entity.setLiftId(repair.getLiftId());
        entity.setPostDate(LocalDateTime.now());

        //设置评价状态
        repair.setHasEvaluate(1);

        return emergencyRepairService.evaluate(repair, entity);
    }

    /**
     * 生成急修单，添加主要，次要急修人签名
     *
     * @param request 请求参数model
     * @return RestResponse
     */
    @PostMapping("repairOrder")
    @Validation(fields = {"id", "recoveryDate", "mainSign", "erRecordImg", "fieldDescription",
            "faultPart", "faultReason", "faultHandle", "faultNature", "faultDuty"})
    public RestResponse repairOrder(@Val @RequestBody RepairRequest request) {
        Long id = request.getId();
        EmergencyRepair entity = emergencyRepairService.getById(id);
        Validate.notNull(entity, ValuePool.EMERGENCY_NOT_EXIST);
        Validate.equals(entity.getStatus(), ValuePool.EMERGENCY_STATE_DOING, ValuePool.EMERGENCY_MUST_IN_DOING);
        LocalDateTime stopDate = entity.getStopDate();
        LocalDateTime recoveryDate = request.getRecoveryDate();
        if(Objects.nonNull(stopDate)){
            Validate.isTrue(stopDate.isBefore(recoveryDate), ValuePool.emergencyProcessTimeIllegal("停梯时间", stopDate));
        }
        List<ErRecordImg> erRecordImgs = new ArrayList<>();
        List<String> erRecordImg = request.getErRecordImg();
        for (int i = 0; i < erRecordImg.size(); i++) {
            ErRecordImg item = new ErRecordImg();
            item.setErRecordId(id);
            item.setImgUrl(erRecordImg.get(i));
            item.setSort(i);
            erRecordImgs.add(item);
        }
        entity.setMainSign(request.getMainSign());
        entity.setSecondSign(request.getSecondSign());
        entity.setFieldDescription(request.getFieldDescription());
        entity.setFaultPart(request.getFaultPart());
        entity.setFaultReason(request.getFaultReason());
        entity.setFaultHandle(request.getFaultHandle());
        entity.setFaultNature(request.getFaultNature());
        entity.setFaultDuty(request.getFaultDuty());
        entity.setRecoveryDate(recoveryDate);
        //修改状态已完成
        entity.setStatus(ValuePool.EMERGENCY_STATE_COMPLETE);
        RestResponse result = emergencyRepairService.repairOrder(entity, erRecordImgs);
        if (Objects.equals("1", result.getStatusCode())) {
            Long mtCompanyId = entity.getMtCompanyId();
            Region region = pushUserService.findRegionByEmergencyRepair(id, mtCompanyId);
            List<Long> userIds = new ArrayList<>();
            if (Objects.nonNull(region)) {
                userIds.addAll(region.members());
            }
            List<PushUserInfo> director = pushUserService.listHighDirector(mtCompanyId);
            List<PushUserInfo> pushUserInfos = new ArrayList<>(director);
            if(!userIds.isEmpty()){
                List<PushUserInfo> userInfos = pushUserService.listByUserIds(mtCompanyId, userIds);
                pushUserInfos.addAll(userInfos);
            }
            if(!pushUserInfos.isEmpty()){
                RepairResponse info = emergencyRepairService.infoById(id);
                PushMessage pushMessage = PushMessage.emergencyOrder(info.getAreaName(), info.getProjectName(),
                        info.getDevicePosition(), info.getRegistrationCode(), info.getCallerDate(), info.getRecoveryDate());
                pushMessage.sendTokenOnPlatform(jmsMessagingTemplate, pushUserInfos);
                //推送消息到pc端
                List<Long> pushUserIds = pushUserInfos.stream().map(PushUserInfo::getUserId).collect(Collectors.toList());
                SendMessageUtil.sendMessageToPC(jmsMessagingTemplate, pushUserIds, pushMessage.getContent(),
                        WebSocketConstants.MessageType.EMERGENCY_MSG);
            }
        }
        return result;
    }

    /**
     * 查询急修的急修图片
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("images")
    @Validation(fields = {"id"})
    public RestResponse images(@Val @RequestBody RepairRequest request) {
        List<ErRecordImg> lists = erRecordImgService.listByErRecordId(request.getId());
        return RestResponse.success(lists);
    }

    @PostMapping("costs")
    @Validation(fields = {"id"})
    public RestResponse costs(@Val @RequestBody RepairRequest request) {
        List<ErRecordCost> costs = erRecordCostService.listByErRecordId(request.getId());
        return RestResponse.success(costs);
    }

    @PostMapping("history")
    @Validation(fields = {"id"})
    public RestResponse history(@Val @RequestBody RepairRequest request) {
        IPage<HistoryResponse> pages = emergencyHistoryService.pageByEmergencyRepair(request);
        return RestResponse.success(pages);
    }

    /**
     * 电梯管理中查询急修记录
     *
     * @param request RepairRequest
     * @return RestResponse
     */
    @PostMapping("listRepair")
    @Validation(fields = {"mtCompanyId", "liftId", "projectId"})
    public RestResponse listRepair(@Val @RequestBody RepairRequest request) {
        IPage<RepairResponse> pages = emergencyRepairService.pageByCompanyLiftProject(request);
        return RestResponse.success(pages);
    }

    private void split(Set<Long> allFaults, Map<String, List<Long>> repairFaults, String id, String key) {
        List<Long> list = splitIds(id);
        allFaults.addAll(list);
        repairFaults.put(key, list);
    }

    private void fillLiftFaults(List<RepairResponse> repairResponses) {
        if (IterUtil.isEmpty(repairResponses)) {
            return;
        }
        Set<Long> allFaults = new HashSet<>();
        Map<String, List<Long>> repairFaults = new HashMap<>();
        repairResponses.forEach(repair -> {
            split(allFaults, repairFaults, repair.getFaultPart(), repair.getId() + "partIds");
            split(allFaults, repairFaults, repair.getFaultReason(), repair.getId() + "reasonIds");
            split(allFaults, repairFaults, repair.getFaultNature(), repair.getId() + "natureIds");
            split(allFaults, repairFaults, repair.getFaultHandle(), repair.getId() + "handleIds");
            split(allFaults, repairFaults, repair.getFaultDuty(), repair.getId() + "dutyIds");
        });
        if (IterUtil.isEmpty(allFaults)) {
            return;
        }
        Collection<LiftFault> liftFaults = liftFaultService.listByIds(allFaults);
        if (IterUtil.isEmpty(liftFaults)) {
            return;
        }
        repairResponses.forEach(repair -> {
            repair.setFaultPart(liftFaults.stream().filter(fault -> repairFaults.get(repair.getId() + "partIds").contains(fault.getId())).map(LiftFault::getFaultDesc).collect(Collectors.joining(",")));
            repair.setFaultReason(liftFaults.stream().filter(fault -> repairFaults.get(repair.getId() + "reasonIds").contains(fault.getId())).map(LiftFault::getFaultDesc).collect(Collectors.joining(",")));
            repair.setFaultNature(liftFaults.stream().filter(fault -> repairFaults.get(repair.getId() + "natureIds").contains(fault.getId())).map(LiftFault::getFaultType).collect(Collectors.joining(",")));
            repair.setFaultHandle(liftFaults.stream().filter(fault -> repairFaults.get(repair.getId() + "handleIds").contains(fault.getId())).map(LiftFault::getFaultType).collect(Collectors.joining(",")));
            repair.setFaultDuty(liftFaults.stream().filter(fault -> repairFaults.get(repair.getId() + "dutyIds").contains(fault.getId())).map(LiftFault::getFaultType).collect(Collectors.joining(",")));
        });
    }

    @PostMapping("print")
    @Validation(fields = {"ids"})
    public RestResponse print(@Val @RequestBody RepairRequest request) {
        List<RepairResponse> repairs = emergencyRepairService.listByIdList(request);
        if (null != repairs && !repairs.isEmpty()) {
            fillLiftFaults(repairs);
            List<ErRecordImg> erRecordImgs = erRecordImgService.listByErRecordIds(request.getIds());
            if (null != erRecordImgs && !erRecordImgs.isEmpty()) {
                for (RepairResponse repair : repairs) {
                    List<ErRecordImg> imgs = erRecordImgs.stream().filter(img -> repair.getId().equals(img.getErRecordId())).collect(Collectors.toList());
                    repair.setErRecordImgs(imgs);
                }
            }
        }
        return RestResponse.success(repairs);
    }

    @PostMapping("export")
    @Validation(fields = {"ids"})
    public void export(@Val @RequestBody RepairRequest request, HttpServletResponse response) {
        log.info("headerAlias: {}", headerAlias);
        List<RepairResponse> repairs = emergencyRepairService.listByIdList(request);
        Validate.notNull(repairs, ValuePool.EMERGENCY_NOT_EXIST);

        fillLiftFaults(repairs);

        List<ErRecordImg> erRecordImgs = erRecordImgService.listByErRecordIds(request.getIds());

        //压缩文件夹
        File baseDir = null;
        File zipFile = null;
        try {
            //获取根目录
            String root = ValuePool.SYSTEM_USER_DIR + File.separator + "repair";
            String missing = WordUtil.imageToBase64Str(root + File.separator + "missing.jpg");
            String generateCode = DateUtils.generateCode();
            String dir = root + File.separator + generateCode;
            String zip = dir + ".zip";
            baseDir = new File(dir);
            zipFile = new File(zip);
            log.info("dir: {}", dir);
            log.info("zip: {}", zip);

            //生成excel文件
            String excel = File.separator + dir + File.separator + generateCode + ".xlsx";
            log.info("生成excel: {}", excel);
            File excelFile = new File(excel);
            ExcelWriter writer = ExcelUtil.getBigWriter(excelFile, "急修记录表");
            //设置列别名
            headerAlias.forEach(writer::addHeaderAlias);
            //如果为true，则不设置alias的字段将不被输出，false表示原样输出
            writer.setOnlyAlias(true);
            writer.write(repairs, true);
//            writer.autoSizeColumnAll();
            writer.close();
            FileUtil.touch(excelFile);

            //处理图片类文件
            //windows现在已知的文件名非法字符有 \ / : * ? " < > |
            String illegalChars = "\\/:*?\"<>|";
            for (int i = 0; i < repairs.size(); i++) {
                RepairResponse repair = repairs.get(i);
                log.info("repair: {}", repair);
                //处理文件夹路径
                String areaName = repair.getAreaName();
                String projectName = repair.getProjectName();
                String registrationCode = repair.getRegistrationCode();
                areaName = StrUtil.isNotEmpty(areaName) ? areaName.replace(illegalChars, "-") : "[区域]";
                projectName = StrUtil.isNotEmpty(projectName) ? projectName.replace(illegalChars, "-") : "[项目]";
                registrationCode = StrUtil.isNotEmpty(registrationCode) ? registrationCode.replace(illegalChars, "-") : "[电梯注册代码]";
                //图片文件夹 dir/i.AreaName-ProjectName-RegistrationCode/
                String imgDir = StrUtil.format("{}{}{}.{}-{}-{}{}", dir, File.separator, (i + 1), areaName, projectName, registrationCode, File.separator);
                List<ErRecordImg> imgs = erRecordImgs.stream().filter(img -> repair.getId().equals(img.getErRecordId())).collect(Collectors.toList());
                if (IterUtil.isNotEmpty(imgs)) {
                    for (int n = 0; n < imgs.size(); n++) {
                        String imgUrl = imgs.get(n).getImgUrl();
                        if (StrUtil.isNotEmpty(imgUrl)) {
                            log.info("imgUrl: {}", imgUrl);
                            String filename = imgDir + StrUtil.format("急修图片{}.jpg", (n + 1));
                            log.info("生成急修图片文件: {}", filename);
                            HttpUtil.downloadFile(imgUrl, filename);
                        }
                    }
                }
                //处理急修签名图片
                String mainSign = repair.getMainSign();
                if (StrUtil.isNotEmpty(mainSign)) {
                    String filename = imgDir + "2.急修签名.jpg";
                    log.info("生成急修签名: {}", filename);
                    HttpUtil.downloadFile(mainSign, filename);
                    repair.setMainSign(WordUtil.imageToBase64Str(filename));
                } else {
                    repair.setMainSign(missing);
                }
                //处理客户签名图片
                String imgUrl = repair.getImgUrl();
                if (StrUtil.isNotEmpty(imgUrl)) {
                    String filename = imgDir + "3.客户签名.jpg";
                    log.info("生成客户签名: {}", filename);
                    HttpUtil.downloadFile(imgUrl, filename);
                    repair.setImgUrl(WordUtil.imageToBase64Str(filename));
                } else {
                    repair.setImgUrl(missing);
                }
                //设置急修单参数
                Map<String, Object> dataMap = new HashMap<>();
                dataMap.put("repair", repair);
                dataMap.put("dateTime", LocalDateTime.now());
                //生成急修单
                String doc = imgDir + "1.急修单.doc";
                log.info("生成急修单: {}", doc);
                WordUtil.createWord(dataMap, root, "repair.doc.ftl", doc);
            }

            //压缩文件夹
            log.info("生成zipfile: {}", zip);
            ZipUtil.zip(dir, zip);
        } catch (Exception e) {
            FileUtil.del(zipFile);
            FileUtil.del(baseDir);
            log.error("生成急修文件失败", e);
            throw Validate.validateException("生成急修文件失败");
        }
        //下载压缩文件
        if (FileUtil.exist(zipFile)) {
            // 设置强制下载不打开, ZIP的application/x-zip-compressed
            response.setContentType("application/force-download");
            // 设置文件名
            response.addHeader("Content-Disposition", "attachment;fileName=" + zipFile.getName());
            byte[] buffer = new byte[1024];
            try (FileInputStream fis = new FileInputStream(zipFile);
                 BufferedInputStream bis = new BufferedInputStream(fis)) {
                OutputStream os = response.getOutputStream();
                int i;
                while ((i = bis.read(buffer)) != -1) {
                    os.write(buffer, 0, i);
                }
            } catch (Exception e) {
                log.error("下载急修文件失败", e);
                throw Validate.validateException("下载急修文件失败");
            } finally {
                //删除临时根目录和zip文件
                FileUtil.del(zipFile);
                FileUtil.del(baseDir);
            }
        }
    }
}
