package org.dromara.elevator.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
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.utils.DistanceUtils;
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.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.elevator.constant.ElevatorConstant;
import org.dromara.elevator.constant.WorkStateEnum;
import org.dromara.elevator.constant.WorkType;
import org.dromara.elevator.domain.*;
import org.dromara.elevator.domain.bo.*;
import org.dromara.elevator.domain.vo.*;
import org.dromara.elevator.mapper.EElevatorInfoMapper;
import org.dromara.elevator.mapper.EMaintenancePlanMapper;
import org.dromara.elevator.mapper.ERelevancyCodeMapper;
import org.dromara.elevator.mapper.EWorkDataMapper;
import org.dromara.elevator.service.*;
import org.dromara.system.domain.SysDept;
import org.dromara.system.mapper.SysDeptMapper;
import org.dromara.system.service.ISysConfigService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static org.dromara.elevator.constant.ElevatorConstant.ON;
import static org.dromara.elevator.constant.ElevatorConstant.WORK_ARRIVE_CONFIG_KEY;
import static org.dromara.elevator.constant.StateConstant.FINISHED;

/**
 * 工作数据信息;Service业务层处理
 *
 * @author zy
 * @date 2024-07-29
 */
@RequiredArgsConstructor
@Service
public class EWorkDataServiceImpl implements IEWorkDataService {

    private final EWorkDataMapper baseMapper;
    private final EElevatorInfoMapper elevatorInfoMapper;
    private final ERelevancyCodeMapper relevancyCodeMapper;
    private final IEWorkOperationRecordService workOperationRecordService;
    private final EMaintenancePlanMapper maintenancePlanMapper;
    private final IEWorkElevatorCabinService workElevatorCabinService;
    private final IEWorkPitService workPitService;
    private final IEWorkCarRoofService workCarRoofService;
    private final IEWorkMachineRoomService workMachineRoomService;
    private final SysDeptMapper deptMapper;
    private final IEWorkerInfoService workerInfoService;
    private final ISysConfigService sysConfigService;

    /**
     * 查询工作数据信息;
     *
     * @param id 主键
     * @return 工作数据信息;
     */
    @Override
    public EWorkDataVo queryById(Long id){
        EWorkDataVo result = baseMapper.selectVoById(id);
        if (Objects.isNull(result)) {
            return result;
        }
        EWorkOperationRecordBo bo = new EWorkOperationRecordBo();
        bo.setElevatorNum(result.getElevatorNum());
        bo.setWorkId(result.getId());
        bo.setStateList(WorkStateEnum.detailStateList());
        List<EWorkOperationRecord> operationRecords = workOperationRecordService.queryOperationRecords(bo);
        if (CollectionUtils.isEmpty(operationRecords)) {
            return result;
        }
        List<EWorkOperationRecordVo> operationRecordVoList = MapstructUtils.convert(operationRecords, EWorkOperationRecordVo.class);
        if (Objects.nonNull(operationRecordVoList)) {
            EElevatorInfo elevatorInfo = geteElevatorInfo(result.getElevatorNum());
            operationRecordVoList = operationRecordVoList.stream()
                .sorted(Comparator.comparing(EWorkOperationRecordVo::getOperationState))
                .peek(item -> {
                    if (Objects.nonNull(item.getLat()) && Objects.nonNull(item.getLng())) {
                        item.setDistance(DistanceUtils.calcDistance(item.getLat(), item.getLng(), elevatorInfo.getLat(), elevatorInfo.getLng()));
                    }
                })
                .collect(Collectors.toList());
        }
        result.setOperationRecords(operationRecordVoList);
        EWorkMachineRoomVo machineRoom = workMachineRoomService.queryByWorId(id);
        result.setMachineRoom(machineRoom);
        result.setCarRoof(workCarRoofService.queryByWorkId(id));
        result.setWorkPit(workPitService.queryByWorkId(id));
        result.setElevatorCabin(workElevatorCabinService.queryByWorkId(id));
        result.setUploadWorkTable(Objects.nonNull(machineRoom));
        return result;
    }

    /**
     * 分页查询工作数据信息;列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 工作数据信息;分页列表
     */
    @Override
    public TableDataInfo<EWorkDataVo> queryPageList(EWorkDataBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<EWorkData> lqw = buildQueryWrapper(bo);
        Page<EWorkDataVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        List<EWorkDataVo> workDataList = result.getRecords();
        if (CollUtil.isNotEmpty(workDataList)) {
            for (EWorkDataVo work : workDataList) {
                if (StringUtils.isNotBlank(work.getRecordUploadData())) {
                    List<RecordUploadBo> pictureList = JsonUtils.parseArray(work.getRecordUploadData(), RecordUploadBo.class);
                    work.setPictureList(pictureList);
                }
                if (Objects.nonNull(work.getArriveTime()) && Objects.nonNull(work.getFinishTime())) {
                    long intervalTime = DateUtil.between(work.getFinishTime(), work.getArriveTime(), DateUnit.SECOND);
                    long diffMin = intervalTime / 60L;
                    long diffSecond = intervalTime % 60L;
                    String maintenanceTime = diffSecond > 0 ? diffMin + "分" + diffSecond + "秒" : diffMin + "分";
                    work.setMaintenanceTime(maintenanceTime);
                }
            }
        }

        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的工作数据信息;列表
     *
     * @param bo 查询条件
     * @return 工作数据信息;列表
     */
    @Override
    public List<EWorkDataVo> queryList(EWorkDataBo bo) {
        LambdaQueryWrapper<EWorkData> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<EWorkData> buildQueryWrapper(EWorkDataBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<EWorkData> lqw = Wrappers.lambdaQuery();
        lqw.in(CollectionUtils.isNotEmpty(bo.getIds()), EWorkData::getId, bo.getIds());
        lqw.eq(bo.getWorkType() != null, EWorkData::getWorkType, bo.getWorkType());
        lqw.eq(StringUtils.isNotBlank(bo.getElevatorNum()), EWorkData::getElevatorNum, bo.getElevatorNum());
        lqw.eq(StringUtils.isNotBlank(bo.getQrCode()), EWorkData::getQrCode, bo.getQrCode());
        lqw.like(StringUtils.isNotBlank(bo.getElevatorName()), EWorkData::getElevatorName, bo.getElevatorName());
        lqw.eq(StringUtils.isNotBlank(bo.getElevatorAddress()), EWorkData::getElevatorAddress, bo.getElevatorAddress());
        lqw.like(StringUtils.isNotBlank(bo.getMaintenance()), EWorkData::getMaintenance, bo.getMaintenance());
        lqw.eq(StringUtils.isNotBlank(bo.getPublisher()), EWorkData::getPublisher, bo.getPublisher());
        // lqw.eq(bo.getCreatedTime() != null, EWorkData::getCreatedTime, bo.getCreatedTime());
        lqw.ge(Objects.nonNull(bo.getCreatedTimeStart()), EWorkData::getCreatedTime, bo.getCreatedTimeStart());
        lqw.le(Objects.nonNull(bo.getCreatedTimeEnd()), EWorkData::getCreatedTime, bo.getCreatedTimeEnd());

        lqw.eq(bo.getExpectTime() != null, EWorkData::getExpectTime, bo.getExpectTime());
        lqw.eq(StringUtils.isNotBlank(bo.getDeadline()), EWorkData::getDeadline, bo.getDeadline());
        lqw.eq(bo.getTrapNum() != null, EWorkData::getTrapNum, bo.getTrapNum());
        lqw.eq(bo.getInjuredNum() != null, EWorkData::getInjuredNum, bo.getInjuredNum());
        lqw.eq(bo.getDieNum() != null, EWorkData::getDieNum, bo.getDieNum());
        lqw.eq(StringUtils.isNotBlank(bo.getFaultCause()), EWorkData::getFaultCause, bo.getFaultCause());
        lqw.eq(StringUtils.isNotBlank(bo.getReason()), EWorkData::getReason, bo.getReason());
        lqw.eq(bo.getLevel() != null, EWorkData::getLevel, bo.getLevel());
        lqw.eq(StringUtils.isNotBlank(bo.getOther()), EWorkData::getOther, bo.getOther());
        lqw.eq(bo.getState() != null, EWorkData::getState, bo.getState());
        lqw.eq(StringUtils.isNotBlank(bo.getCollaborator()), EWorkData::getCollaborator, bo.getCollaborator());
        lqw.eq(StringUtils.isNotBlank(bo.getSignatureState()), EWorkData::getSignatureState, bo.getSignatureState());
        lqw.eq(StringUtils.isNotBlank(bo.getDescription()), EWorkData::getDescription, bo.getDescription());
        lqw.eq(StringUtils.isNotBlank(bo.getAttachment()), EWorkData::getAttachment, bo.getAttachment());
        lqw.eq(StringUtils.isNotBlank(bo.getOperatingRecord()), EWorkData::getOperatingRecord, bo.getOperatingRecord());
        lqw.in(CollectionUtils.isNotEmpty(bo.getStateList()), EWorkData::getState, bo.getStateList());
        lqw.ge(Objects.nonNull(bo.getMaintenanceStartTime()), EWorkData::getExpectTime, bo.getMaintenanceStartTime());
        lqw.le(Objects.nonNull(bo.getMaintenanceEndTime()), EWorkData::getExpectTime, bo.getMaintenanceEndTime());
        lqw.eq(Objects.nonNull(bo.getRepairAudit()), EWorkData::getRepairAudit, bo.getRepairAudit());
        lqw.eq(Objects.nonNull(bo.getPlanType()), EWorkData::getPlanType, bo.getPlanType());

        if (bo.getOverdueFlag() && Objects.nonNull(bo.getExpectTime())) {
            // 超期未完成
            lqw.lt(EWorkData::getExpectTime, bo.getExpectTime());
        }

        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(EWorkData::getDeptId, ids);
        });
        if (bo.getMiniSearchFlag()) {
            LocalDate searchTime = LocalDate.of(2025, 1, 1);
            lqw.ge(EWorkData::getExpectTime, searchTime);
        }
        if (bo.getMiniSearchFlag()
            && (Objects.equals(bo.getWorkType(), WorkType.MAINTENANCE.getType())
            || Objects.equals(bo.getWorkType(), WorkType.REPAIR.getType()))) {

            if (CollectionUtils.isNotEmpty(bo.getStateList())
                && bo.getStateList().contains(WorkStateEnum.COMPLETE.getState())) {
                // 已完成
                lqw.orderByDesc(EWorkData::getExpectTime).orderByAsc(EWorkData::getState);
            } else {
                lqw.orderByAsc(EWorkData::getExpectTime, EWorkData::getState).orderByDesc(EWorkData::getCreatedTime);
            }
        } else {
            lqw.orderByAsc(EWorkData::getState, EWorkData::getExpectTime).orderByDesc(EWorkData::getCreatedTime);
        }
        return lqw;
    }

    /**
     * 新增工作数据信息;
     *
     * @param bo 工作数据信息;
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(EWorkDataBo bo) {
        EWorkData add = MapstructUtils.convert(bo, EWorkData.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改工作数据信息;
     *
     * @param bo 工作数据信息;
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(EWorkDataBo bo) {
        EWorkData update = MapstructUtils.convert(bo, EWorkData.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

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

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

    /**
     * 派发工作数据
     * @param bo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean distributionWork(DistributionWorkBo bo) {
        if (CollUtil.isEmpty(bo.getElevatorList())) {
            throw new ServiceException("电梯信息不能为空!");
        }
        if (Objects.isNull(bo.getHappenTime())) {
            bo.setHappenTime(new Date());
        }
        List<EWorkData> workList = new ArrayList<>();
        List<EMaintenancePlan> planList = new ArrayList<>();
        for (DistributionWorkBo.Elevator elevator : bo.getElevatorList()) {
            EElevatorInfo elevatorInfo = elevatorInfoMapper.selectById(elevator.getId());
            EWorkData workData = new EWorkData();
            workData.setWorkType(bo.getWorkType());
            workData.setExpectTime(bo.getExpectTime());
            workData.setElevatorName(elevatorInfo.getElevatorName());
            workData.setElevatorNum(elevatorInfo.getElevatorNum());
            workData.setElevatorAddress(elevatorInfo.getElevatorAddress());
            workData.setRegisterCode(elevatorInfo.getRegisterCode());
            workData.setDeptId(elevatorInfo.getDeptId());
            workData.setHappenTime(bo.getHappenTime());
            workData.setState(WorkStateEnum.NEW.getState());
            workData.setDescription(StringUtils.isBlank(bo.getDescription()) ? "领导派发" : bo.getDescription());
            workData.setPlanType(bo.getPlanType());
            workData.setRepairMaterialData(bo.getRepairMaterialData());
            workData.setMaterialQuote(bo.getMaterialQuote());
            workData.setOtherExpenses(bo.getOtherExpenses());
            workData.setTotalBudget(bo.getTotalBudget());
            workData.setRepairBeforeRecord(bo.getRepairBeforeRecord());
            workData.setPublisher(LoginHelper.getUsername());
            workData.setMaintenance(elevatorInfo.getMaintenanceName());

            workData.setCreatedTime(new Date());
            // 获取二维码信息
            LambdaQueryWrapper<ERelevancyCode> query = new LambdaQueryWrapper<>();
            query.eq(ERelevancyCode::getElevatorNum, elevatorInfo.getElevatorNum());
            ERelevancyCode code = relevancyCodeMapper.selectOne(query);
            if (Objects.nonNull(code)) {
                workData.setQrCode(code.getQrCode());
            }
            workList.add(workData);

            if (WorkType.MAINTENANCE.getType().equals(bo.getWorkType())) {
                EMaintenancePlan plan = new EMaintenancePlan();
                plan.setElevatorNum(elevatorInfo.getElevatorNum());
                plan.setPlanTime(bo.getExpectTime());
                plan.setPlanType(bo.getPlanType());
                plan.setDeptId(elevatorInfo.getDeptId());
                plan.setState(0);
                plan.setFinishState(0);
                planList.add(plan);
            }
        }
        if (CollectionUtils.isNotEmpty(planList)) {
            maintenancePlanMapper.insertBatch(planList);
        }
        return baseMapper.insertBatch(workList);
    }

    /**
     * 超期维保
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<EWorkDataVo> overduePageList(EWorkDataBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<EWorkData> query = Wrappers.lambdaQuery();
        query.lt( EWorkData::getExpectTime, LocalDate.now());
        query.eq(EWorkData::getState, WorkStateEnum.NEW.getState());
        Page<EWorkDataVo> result = baseMapper.selectVoPage(pageQuery.build(), query);

        List<EWorkDataVo> workDataList = result.getRecords();
        if (CollUtil.isNotEmpty(workDataList)) {
            for (EWorkDataVo workData : workDataList) {
                // 获取上次维保日期
                LambdaQueryWrapper<EWorkData> queryLast = Wrappers.lambdaQuery();
                queryLast.eq(EWorkData::getElevatorNum, workData.getElevatorNum());
                queryLast.eq(EWorkData::getWorkType, WorkType.MAINTENANCE.getType());
                queryLast.eq(EWorkData::getState, WorkStateEnum.COMPLETE.getState());
                queryLast.orderByDesc(EWorkData::getExpectTime);
                queryLast.last(" limit 1 ");
                EWorkDataVo vo = baseMapper.selectVoOne(queryLast);
                if (Objects.nonNull(vo)) {
                    workData.setLastTime(vo.getExpectTime());
                }

                // 超出天数
                long days = ChronoUnit.DAYS.between(workData.getExpectTime(), LocalDate.now());
                workData.setExceedDay(days);
            }
        }
        result.setRecords(workDataList);
        return TableDataInfo.build(result);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean receive(WorkOperationBo bo) {
        EWorkData workData = this.getAndCheckState(bo.getWorkId(), WorkStateEnum.NEW);

        WorkStateEnum receive = WorkStateEnum.RECEIVE;
        workData.setState(receive.getState());
        workData.setReceiveStaff(LoginHelper.getUsername());
        if (Objects.isNull(bo.getReceiveTime())) {
            bo.setReceiveTime(new Date());
        }
        if (Objects.nonNull(bo.getCollaborator())) {
            EWorkerInfoVo workerInfoVo = workerInfoService.queryById(Long.valueOf(bo.getCollaborator()));
            if (Objects.nonNull(workerInfoVo)) {
                workData.setCollaborator(workerInfoVo.getPhoneName());
            }
        }
        workData.setReceiveTime(bo.getReceiveTime());
        EWorkOperationRecordBo operationRecordBo = buildWorkOperation(bo, workData, receive);

        this.workOperationRecordService.insertByBo(operationRecordBo);
        this.baseMapper.updateById(workData);
        return Boolean.TRUE;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean arrive(WorkOperationBo bo) {
        EWorkData workData = this.getAndCheckState(bo.getWorkId(), WorkStateEnum.RECEIVE);
        // 经纬度校验 距离
        checkArriveDistance(bo, workData);

        WorkStateEnum arrive = WorkStateEnum.ARRIVE;
        workData.setState(arrive.getState());
        workData.setArriveTime(new Date());

        EWorkOperationRecordBo operationRecordBo = this.buildWorkOperation(bo, workData, arrive);
        this.workOperationRecordService.insertByBo(operationRecordBo);
        this.baseMapper.updateById(workData);
        return Boolean.TRUE;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean workRecord(WorkOperationBo bo) {
        Long workId = bo.getMachineRoomBo().getWorkId();
        EWorkMachineRoomVo machineRoomVo = workMachineRoomService.queryByWorId(workId);
        if (Objects.nonNull(machineRoomVo)) {
            bo.getMachineRoomBo().setId(machineRoomVo.getId());
            workMachineRoomService.updateByBo(bo.getMachineRoomBo());
        } else {
            workMachineRoomService.insertByBo(bo.getMachineRoomBo());
        }
        EWorkPitVo workPitVo = workPitService.queryByWorkId(bo.getPitBo().getWorkId());
        if (Objects.nonNull(workPitVo)) {
            bo.getPitBo().setId(workPitVo.getId());
            workPitService.updateByBo(bo.getPitBo());
        } else {
            workPitService.insertByBo(bo.getPitBo());
        }
        EWorkCarRoofVo carRoofVo = workCarRoofService.queryByWorkId(bo.getCarRoofBo().getWorkId());
        if (Objects.nonNull(carRoofVo)) {
            bo.getCarRoofBo().setId(carRoofVo.getId());
            workCarRoofService.updateByBo(bo.getCarRoofBo());
        } else {
            workCarRoofService.insertByBo(bo.getCarRoofBo());
        }
        EWorkElevatorCabinVo workElevatorCabinVo = workElevatorCabinService.queryByWorkId(bo.getElevatorCabinBo().getWorkId());
        if (Objects.nonNull(workElevatorCabinVo)) {
            bo.getElevatorCabinBo().setId(workElevatorCabinVo.getId());
            workElevatorCabinService.updateByBo(bo.getElevatorCabinBo());
        } else {
            workElevatorCabinService.insertByBo(bo.getElevatorCabinBo());
        }
        return Boolean.TRUE;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean complete(WorkOperationBo bo) {
        WorkStateEnum checkState = Objects.equals(bo.getWorkType(), WorkType.RESCUE.getType()) ? WorkStateEnum.RECEIVE : WorkStateEnum.ARRIVE;

        EWorkData workData = this.getAndCheckState(bo.getWorkId(), checkState);

        if (Objects.equals(workData.getWorkType(), WorkType.MAINTENANCE.getType())) {
            EWorkMachineRoomVo workMachineRoom = workMachineRoomService.queryByWorId(bo.getWorkId());
            if (Objects.isNull(workMachineRoom)) {
                throw new ServiceException("该工单工作表未提交");
            }
        }

        WorkStateEnum complete = WorkStateEnum.COMPLETE;
        workData.setState(complete.getState());
        if (Objects.isNull(bo.getFinishTime())) {
            bo.setFinishTime(new Date());
        }
        workData.setFinishTime(bo.getFinishTime());
        workData.setSignDate(new Date());
        workData.setSignRecord(bo.getSignRecord());
        workData.setDoorAttachment(bo.getDoorAttachment());
        workData.setPitAttachment(bo.getPitAttachment());
        workData.setCabinAttachment(bo.getCabinAttachment());
        workData.setMachineRoomAttachment(bo.getMachineRoomAttachment());
        workData.setCarRoofAttachment(bo.getCarRoofAttachment());
        workData.setChannelAttachment(bo.getChannelAttachment());
        workData.setRepairAfterRecord(bo.getRepairAfterRecord());
        workData.setRepairReplaceRecord(bo.getRepairReplaceRecord());
        workData.setFaultCause(bo.getFaultCause());
        workData.setReason(bo.getReason());

        EWorkOperationRecordBo operationRecordBo = this.buildWorkOperation(bo, workData, complete);
        this.updateMaintenancePlan(workData);
        this.workOperationRecordService.insertByBo(operationRecordBo);
        this.baseMapper.updateById(workData);
        return Boolean.TRUE;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean evaluate(WorkOperationBo bo) {
        EWorkData workData = this.getAndCheckState(bo.getWorkId(), WorkStateEnum.COMPLETE);
        WorkStateEnum evaluate = WorkStateEnum.EVALUATE;
        workData.setState(evaluate.getState());

        EWorkOperationRecordBo operationRecordBo = this.buildWorkOperation(bo, workData, evaluate);
        this.workOperationRecordService.insertByBo(operationRecordBo);
        this.baseMapper.updateById(workData);
        return Boolean.TRUE;
    }

    @Override
    public boolean updateExpectTime(EWorkDataBo bo) {
        EWorkData workData = getAndCheckState(bo.getId(), null);
        if (bo.getState() > WorkStateEnum.RECEIVE.getState()) {
            throw new ServiceException("工单状态不正确!");
        }
        workData.setOriginalExpectTime(workData.getExpectTime());
        workData.setExpectTime(bo.getExpectTime());
        workData.setIsModifyExpect(1);
        workData.setModifyExpectRemark(bo.getModifyExpectRemark());
        return this.baseMapper.updateById(workData) > 0;
    }

    @Override
    public boolean uploadRecord(Long workId, List<RecordUploadBo> recordUploadBoList) {
        EWorkData workData = this.getAndCheckState(workId, null);
        List<RecordUploadBo> list = new ArrayList<>();
        if (StringUtils.isNotBlank(workData.getRecordUploadData())) {
            list = JsonUtils.parseArray(workData.getRecordUploadData(), RecordUploadBo.class);
        }
        list.addAll(recordUploadBoList);
        for (RecordUploadBo item : list) {
            if (Objects.isNull(item.getUploadTime())) {
                item.setUploadTime(new Date());
            }
        }
        workData.setRecordUploadData(JsonUtils.toJsonString(list));
        return this.baseMapper.updateById(workData) > 0;
    }

    /**
     * 导出维保数据及图片
     * @param param
     * @param resp
     */
    //@Override
    public void exportPictureTwo(EWorkDataBo param, HttpServletResponse resp) throws IOException {
        LambdaQueryWrapper<EWorkData> lqw = buildQueryWrapper(param);
        List<EWorkDataVo> workDataList = 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("maintenance", "UTF-8") + ".xlsx");
        ServletOutputStream out= resp.getOutputStream();
        try {
            SimpleDateFormat time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Sheet sheet = workbook.createSheet("维保数据");
            // 创建标题行
            Row headerRow = sheet.createRow(0);
            headerRow.createCell(0).setCellValue("工作状态");
            headerRow.createCell(1).setCellValue("电梯位置");
            headerRow.createCell(2).setCellValue("电梯注册代码");
            headerRow.createCell(3).setCellValue("描述");
            headerRow.createCell(4).setCellValue("创建时间");
            headerRow.createCell(5).setCellValue("期望时间");
            headerRow.createCell(6).setCellValue("状态");
            int rowNumOne = 1;
            for (EWorkDataVo work : workDataList) {
                Row row = sheet.createRow(rowNumOne++);
                sheet.setDefaultRowHeight((short) 500);
                sheet.setDefaultColumnWidth(20);
                row.createCell(0).setCellValue("维保");
                row.createCell(1).setCellValue(work.getElevatorName());
                row.createCell(2).setCellValue(work.getRegisterCode());
                row.createCell(3).setCellValue(work.getDescription());
                row.createCell(4).setCellValue(time.format(work.getCreateTime()));
                row.createCell(5).setCellValue(time.format(Date.from(work.getExpectTime().atStartOfDay(ZoneId.systemDefault()).toInstant())));
                row.createCell(6).setCellValue(WorkStateEnum.getValueByKey(work.getState()));
            }

            Sheet sheetTwo = workbook.createSheet("维保图片");
            Row pictureHeaderRow = sheetTwo.createRow(0);
            pictureHeaderRow.createCell(0).setCellValue("电梯名称");
            pictureHeaderRow.createCell(1).setCellValue("创建时间");
            // 数据信息
            int rowNum = 1;
            for (EWorkDataVo work : workDataList) {
                Row row = sheetTwo.createRow(rowNum++);
                row.createCell(0).setCellValue(work.getElevatorName());
                row.createCell(1).setCellValue(time.format(work.getCreateTime()));
                sheetTwo.setDefaultRowHeight((short) 800);
                sheetTwo.setDefaultColumnWidth(20);
                // 获取图片数据
                List<String> pictures = new ArrayList<>();
                if (StringUtils.isNotBlank(work.getRecordUploadData())) {
                    List<RecordUploadBo> pictureList = JsonUtils.parseArray(work.getRecordUploadData(), RecordUploadBo.class);
                    for (RecordUploadBo record : pictureList) {
                        // 图片
                        if (Objects.equals(ElevatorConstant.PICTURE, record.getAttachmentType())) {
                            pictures.add(record.getAttachmentUrl());
                        }
                    }
                }
                if (CollUtil.isNotEmpty(pictures)) {
                    for (int i = 0 ; i < pictures.size() ; i++) {
                        // 下载照片到临时文件
                        String tempFileName = UUID.randomUUID() + ".jpg";
                        File tempFile = new File(tempFileName);
                        HttpUtil.downloadFile(pictures.get(i), 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 = sheetTwo.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();
        }
    }

    /**
     * 导出维保数据及图片
     * @param param
     * @param resp
     */
    @Override
    public void exportPicture(EWorkDataBo param, HttpServletResponse resp) throws IOException {
        LambdaQueryWrapper<EWorkData> lqw = buildQueryWrapper(param);
        List<EWorkDataVo> workDataList = 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("maintenance", "UTF-8") + ".xlsx");
        ServletOutputStream out= resp.getOutputStream();
        try {
            SimpleDateFormat time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            Sheet sheet = workbook.createSheet("维保数据");
            // 创建标题行
            Row headerRow = sheet.createRow(0);
            headerRow.createCell(0).setCellValue("工作状态");
            headerRow.createCell(1).setCellValue("电梯位置");
            headerRow.createCell(2).setCellValue("电梯注册代码");
            headerRow.createCell(3).setCellValue("描述");
            headerRow.createCell(4).setCellValue("创建时间");
            headerRow.createCell(5).setCellValue("期望时间");
            headerRow.createCell(6).setCellValue("状态");
            int rowNumOne = 1;
            for (EWorkDataVo work : workDataList) {
                Row row = sheet.createRow(rowNumOne++);
                sheet.setDefaultRowHeight((short) 500);
                sheet.setDefaultColumnWidth(20);
                row.createCell(0).setCellValue("维保");
                row.createCell(1).setCellValue(work.getElevatorName());
                row.createCell(2).setCellValue(work.getRegisterCode());
                row.createCell(3).setCellValue(work.getExpectTime().format(formatter) + "日维保");
                row.createCell(4).setCellValue(time.format(work.getCreateTime()));
                row.createCell(5).setCellValue(work.getExpectTime().format(formatter));
                row.createCell(6).setCellValue(WorkStateEnum.getValueByKey(work.getState()));
            }

            Sheet sheetTwo = workbook.createSheet("维保图片");
            Row pictureHeaderRow = sheetTwo.createRow(0);
            pictureHeaderRow.createCell(0).setCellValue("电梯名称");
            pictureHeaderRow.createCell(1).setCellValue("创建时间");
            pictureHeaderRow.createCell(2).setCellValue("轿顶附件");
            pictureHeaderRow.createCell(3).setCellValue("轿厢附件");
            pictureHeaderRow.createCell(4).setCellValue("机房附件");
            pictureHeaderRow.createCell(5).setCellValue("底坑附件");
            pictureHeaderRow.createCell(6).setCellValue("轿门附件");
            pictureHeaderRow.createCell(7).setCellValue("井道附件");
            // 数据信息
            int rowNum = 1;
            for (EWorkDataVo work : workDataList) {
                Row row = sheetTwo.createRow(rowNum++);
                row.createCell(0).setCellValue(work.getElevatorName());
                row.createCell(1).setCellValue(work.getExpectTime().format(formatter));
                sheetTwo.setDefaultRowHeight((short) 800);
                sheetTwo.setDefaultColumnWidth(20);

                // 插入图片数据
                if (StringUtils.isNotBlank(work.getCarRoofAttachment())) {
                    bulidPicture(workbook, sheetTwo, rowNum, 2, 3, work.getCarRoofAttachment());
                }
                if (StringUtils.isNotBlank(work.getCabinAttachment())) {
                    bulidPicture(workbook, sheetTwo, rowNum, 3, 4, work.getCabinAttachment());
                }
                if (StringUtils.isNotBlank(work.getMachineRoomAttachment())) {
                    bulidPicture(workbook, sheetTwo, rowNum, 4, 5, work.getMachineRoomAttachment());
                }
                if (StringUtils.isNotBlank(work.getPitAttachment())) {
                    bulidPicture(workbook, sheetTwo, rowNum, 5, 6, work.getPitAttachment());
                }
                if (StringUtils.isNotBlank(work.getDoorAttachment())) {
                    bulidPicture(workbook, sheetTwo, rowNum, 6, 7, work.getDoorAttachment());
                }
                if (StringUtils.isNotBlank(work.getChannelAttachment())) {
                    bulidPicture(workbook, sheetTwo, rowNum, 7, 8, work.getChannelAttachment());
                }
            }
            workbook.write(out);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            workbook.close();
            out.close();
        }
    }

    @Override
    public boolean auditRepair(EWorkDataBo bo) {
        EWorkData workData = baseMapper.selectById(bo.getId());
        if (Objects.isNull(workData)) {
            return Boolean.FALSE;
        }
        // 审核完成
        workData.setMaterialQuote(bo.getMaterialQuote());
        workData.setOtherExpenses(bo.getOtherExpenses());
        workData.setTotalBudget(bo.getTotalBudget());
        workData.setRepairAudit(FINISHED);
        // 操作记录
        workOperationRecordService.insertByBo(buildWorkOperation(new WorkOperationBo(), workData, WorkStateEnum.NEW));

        return baseMapper.updateById(workData) > 0;
    }

    @Override
    public Boolean auditMaintenance(EWorkDataBo bo) {
        EWorkData workData = baseMapper.selectById(bo.getId());
        if (Objects.isNull(workData)) {
            return Boolean.FALSE;
        }
        workData.setMaintenanceAudit(bo.getMaintenanceAudit());

        return baseMapper.updateById(workData) > 0;
    }

    private static void bulidPicture(Workbook workbook, Sheet sheetTwo, int rowNum, int start, int end, String url) throws IOException {
        // 下载照片到临时文件
        String tempFileName = UUID.randomUUID() + ".jpg";
        File tempFile = new File(tempFileName);
        HttpUtil.downloadFile(url, 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 = sheetTwo.createDrawingPatriarch();
        ClientAnchor anchor = helper.createClientAnchor();

        anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_DONT_RESIZE);
        anchor.setCol1(start);
        anchor.setRow1(rowNum - 1);
        // 图片结束列
        anchor.setCol2(end);
        // 图片结束行
        anchor.setRow2(rowNum);
        Picture picture = drawing.createPicture(anchor, pictureIdx);
        picture.resize(1,1);
        inputStream.close();
        tempFile.delete();
    }

    /**
     * 获取并校验 工作信息
     *
     * @param workId          工作id
     * @param beforeStateEnum 前置工作状态
     * @return 工作信息
     */
    private EWorkData getAndCheckState(Long workId, WorkStateEnum beforeStateEnum) {
        EWorkData workData = this.baseMapper.selectById(workId);
        if (Objects.isNull(workData)) {
            throw new ServiceException("未查到该工单信息");
        }

        if (Objects.nonNull(beforeStateEnum) && !beforeStateEnum.getState().equals(workData.getState())) {
            throw new ServiceException("工单状态不正确");
        }
        return workData;
    }


    /**
     * 构建操作记录信息
     *
     * @param bo        操作信息
     * @param workData  工作数据
     * @param stateEnum 工作状态
     * @return 操作记录
     */
    private EWorkOperationRecordBo buildWorkOperation(WorkOperationBo bo, EWorkData workData, WorkStateEnum stateEnum) {
        EWorkOperationRecordBo operationRecordBo = new EWorkOperationRecordBo();
        operationRecordBo.setOperator(LoginHelper.getUsername());
        operationRecordBo.setOperationTime(new Date());
        operationRecordBo.setElevatorNum(workData.getElevatorNum());
        operationRecordBo.setWorkId(workData.getId());
        operationRecordBo.setWorkType(workData.getWorkType());
        operationRecordBo.setOperationState(workData.getState());
        operationRecordBo.setOperatorId(LoginHelper.getUserId());
        if (StringUtils.isBlank(bo.getOperationContent())) {
            bo.setOperationContent(stateEnum.getDesc());
        }
        operationRecordBo.setLat(bo.getLat());
        operationRecordBo.setLng(bo.getLng());
        operationRecordBo.setOperationContent(bo.getOperationContent());
        operationRecordBo.setDeptId(LoginHelper.getDeptId());
        EElevatorInfo elevatorInfo = this.geteElevatorInfo(workData.getElevatorNum());
        if (Objects.nonNull(elevatorInfo)) {
            operationRecordBo.setMaintenanceId(Long.valueOf(elevatorInfo.getMaintenanceId()));
            operationRecordBo.setTenementId(Long.valueOf(elevatorInfo.getTenementId()));
        }
        return operationRecordBo;
    }

    /**
     * 更新维保计划
     *
     * @param workData 工作数据
     */
    private void updateMaintenancePlan(EWorkData workData) {
        if (!WorkType.MAINTENANCE.getType().equals(workData.getWorkType())) {
            return;
        }
        EMaintenancePlan maintenancePlan = maintenancePlanMapper.selectOne(Wrappers.<EMaintenancePlan>lambdaQuery()
            .eq(EMaintenancePlan::getElevatorNum, workData.getElevatorNum())
            .eq(EMaintenancePlan::getPlanTime, workData.getExpectTime())
            .last(" limit 1 "));
        if (Objects.isNull(maintenancePlan)) {
            return;
        }
        maintenancePlan.setFinishState(1);
        maintenancePlanMapper.updateById(maintenancePlan);
    }

    /**
     * 获取电梯信息
     *
     * @param elevatorNum 电梯编号
     * @return 电梯信息
     */
    private EElevatorInfo geteElevatorInfo(String elevatorNum) {
        return this.elevatorInfoMapper.selectOne(Wrappers.<EElevatorInfo>lambdaQuery()
            .eq(EElevatorInfo::getElevatorNum, elevatorNum));
    }

    /**
     * 校验到场范围
     *
     * @param bo       业务参数
     * @param workData 工作数据
     */
    private void checkArriveDistance(WorkOperationBo bo, EWorkData workData) {
        if (!Objects.equals(workData.getWorkType(), WorkType.MAINTENANCE.getType())
            && !Objects.equals(workData.getWorkType(), WorkType.REPAIR.getType())) {
            return;
        }
        String arriveConfig = this.sysConfigService.selectConfigByKey(WORK_ARRIVE_CONFIG_KEY);
        WorkArriveConfigBo configBo = JsonUtils.parseObject(arriveConfig, WorkArriveConfigBo.class);
        if (Objects.isNull(configBo) || !Objects.equals(configBo.getEnabled(), ON)) {
            return;
        }

        EElevatorInfo elevatorInfo = this.geteElevatorInfo(workData.getElevatorNum());
        if (Objects.nonNull(elevatorInfo) && Objects.nonNull(elevatorInfo.getLat()) && Objects.nonNull(elevatorInfo.getLng())) {
            BigDecimal distance = DistanceUtils.calcDistance(bo.getLat(), bo.getLng(), elevatorInfo.getLat(), elevatorInfo.getLng());
            // 大于配置距离范围
            if (distance.compareTo(configBo.getDistance()) > 0) {
                throw new ServiceException(String.format("距离工作范围超出%s米!", configBo.getDistance()));
            }
        }
    }
}
