package com.yeejoin.amos.boot.module.jyjc.biz.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.yeejoin.amos.boot.biz.common.bo.ReginParams;
import com.yeejoin.amos.boot.biz.common.entity.DataDictionary;
import com.yeejoin.amos.boot.biz.common.service.impl.DataDictionaryServiceImpl;
import com.yeejoin.amos.boot.module.common.api.dao.ESEquipmentCategory;
import com.yeejoin.amos.boot.module.common.api.dto.ESEquipmentCategoryDto;
import com.yeejoin.amos.boot.module.common.api.enums.EquipmentClassifityEnum;
import com.yeejoin.amos.boot.module.jg.api.enums.BusinessTypeEnum;
import com.yeejoin.amos.boot.module.jg.api.enums.WorkFlowStatusEnum;
import com.yeejoin.amos.boot.module.jyjc.api.entity.JyjcCmTask;
import com.yeejoin.amos.boot.module.jyjc.api.entity.JyjcInspectionApplication;
import com.yeejoin.amos.boot.module.jyjc.api.entity.JyjcInspectionApplicationEquip;
import com.yeejoin.amos.boot.module.jyjc.api.model.*;
import com.yeejoin.amos.boot.module.jyjc.biz.service.TaskModelServiceImpl;
import com.yeejoin.amos.boot.module.ymt.api.entity.IdxBizJgProjectContraption;
import com.yeejoin.amos.boot.module.ymt.api.enums.FlowStatusEnum;
import com.yeejoin.amos.boot.module.ymt.api.mapper.IdxBizJgProjectContraptionMapper;
import com.yeejoin.amos.feign.workflow.model.ProcessTaskDTO;
import com.yeejoin.amos.feign.workflow.model.TaskResultDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.rdbms.orm.entity.BaseEntity;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Administrator
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class InspectionPlanServiceImpl  extends InspectionPlanCommonServiceImpl {


    private final RedissonClient redissonClient;
    private final IdxBizJgProjectContraptionMapper idxBizJgProjectContraptionMapper;

    private final JyjcInspectionApplicationServiceImpl inspectionApplicationService;

    private final JyjcInspectionApplicationEquipServiceImpl applicationEquipService;

    private final JyjcInspectionResultServiceImpl inspectionResultService;

    private final ESEquipmentCategory esEquipmentCategory;

    private final CommonServiceImpl commonService;

    private final CmWorkflowServiceImpl cmWorkflowService;

    private final TaskModelServiceImpl taskModelService;

    private final DataDictionaryServiceImpl dataDictionaryService;

    private final JyjcCmTaskServiceImpl cmTaskService;

    private final  WorkBenchTaskCreateServiceImpl workBenchTaskCreateService;

    private final static String msgTpl = "%s申请单号：%s，进行%s";

    /**
     * 通过
     */
    private final static String OPERATE_PASS = "0";

    /**
     * 驳回
     */
    private final static String OPERATE_REJECT = "1";

    /**
     * 撤回
     */
    private final static String OPERATE_WITHDRAW = "2";



    public InspectionPlanModel savePlanData(String applicationSeq, InspectionPlanModel model) {
        LambdaUpdateWrapper<JyjcInspectionApplication> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(JyjcInspectionApplication::getPlanData, JSONObject.toJSONString(model));
        updateWrapper.eq(BaseEntity::getSequenceNbr, applicationSeq);
        updateWrapper.set(JyjcInspectionApplication::getPlanCreateDate, new Date());
        inspectionApplicationService.update(updateWrapper);
        // 异步写入计划排期日期，统计办理时效使用
        this.updatePlanCreateDate(applicationSeq);
        // todo 注意此处编辑后的数据不再回写道公共台的原始数据中，为反应原始审批信息，原始数据在审批完成后就已经进行固化，不再随这修改
        return model;
    }

    private void updatePlanCreateDate(String applicationSeq) {
        JyjcInspectionApplication inspectionApplication = inspectionApplicationService.getById(applicationSeq);
        inspectionResultService.updatePlanCreateDateByAppNo(inspectionApplication.getApplicationNo());
    }

    public JSONObject getDetail(String applicationSeq) {
        JyjcInspectionApplication inspectionApplication = inspectionApplicationService.getById(applicationSeq);
        // 初始时查询设备地址关联设备表
        JSONObject jsonObject = inspectionApplication.getPlanData();
        if (jsonObject == null) {
            jsonObject = new JSONObject();
            String address = this.getOneEquipAddress(inspectionApplication);
            jsonObject.put("address", address);
        }
        jsonObject.put("applicationSeq", inspectionApplication.getSequenceNbr());
        jsonObject.remove("applyInfo");
        jsonObject.remove("formData");
        return jsonObject;
    }

    private String getOneEquipAddress(JyjcInspectionApplication application) {
        // 压力管道取装置使用/安装地址
        if (EquipmentClassifityEnum.YLGD.getCode().equals(application.getEquipClassify())) {
            IdxBizJgProjectContraption projectContraption = idxBizJgProjectContraptionMapper.selectById(application.getProjectContraptionId());
            return Stream.of(projectContraption.getProvinceName(), projectContraption.getCityName(), projectContraption.getCountyName(), projectContraption.getStreetName(), projectContraption.getAddress())
                    .map(value -> value == null ? "" : value)
                    .collect(Collectors.joining());
        } else {
            // 其他取第一个设备使用/安装地址
            List<JyjcInspectionApplicationEquip> applicationEquips = applicationEquipService.list(new LambdaQueryWrapper<JyjcInspectionApplicationEquip>().eq(JyjcInspectionApplicationEquip::getApplicationSeq, application.getSequenceNbr()));
            if (!applicationEquips.isEmpty()) {
                Optional<ESEquipmentCategoryDto> optional = esEquipmentCategory.findById(applicationEquips.get(0).getEquipUnicode());
                if (optional.isPresent()) {
                    ESEquipmentCategoryDto esEquipmentCategoryDto = optional.get();
                    return this.concatDetailAddress(esEquipmentCategoryDto);
                }
            }
            return "";
        }
    }

    private String concatDetailAddress(ESEquipmentCategoryDto esEquipmentCategoryDto) {
        return Stream.of(esEquipmentCategoryDto.getUSE_PLACE(), esEquipmentCategoryDto.getADDRESS()).map(a -> a == null ? "" : a.replace("/", "").trim()).collect(Collectors.joining());
    }

    public JSONObject getWorkBenchDetail(String applicationSeq) {
        JSONObject res = new JSONObject();
        JyjcCmTask jyjcCmTask = cmTaskService.getOneBySSeq(String.valueOf(applicationSeq));
        BeanUtil.copyProperties(jyjcCmTask, res);
        res.putAll(Optional.ofNullable(JSONObject.parseObject(jyjcCmTask.getFormData())).orElse(new JSONObject()));
        Map<String, Object> applyInfo = inspectionApplicationService.selectBySeq(Long.parseLong(applicationSeq), null);
        applyInfo.remove("planData");
        res.put("applyInfo", applyInfo);
        res.put("inspectionUnitCode", applyInfo.get("inspectionUnitCode"));
        res.remove("operate");
        res.remove("comment");
        return res;
    }

    public void flowExecute(Long applicationSeq, String instanceId, String operate, String comment, String nextTaskId, JSONObject params, ReginParams reginParams) {
        String lockKey = CommonServiceImpl.buildJcExecuteLockKey(instanceId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            // 解决并发问题：多个人同时操作一个流程（并发执行通过、驳回、撤回）
            if (!isLocked) {
                throw new BadRequest("当前流程已经被执行！请重新打开页面查看并执行！");
            }
            // 流程执行时，状态及权限校验
            commonService.checkForExecuteFlow(nextTaskId, instanceId);
            InspectionPlanModel planModel = JSONObject.parseObject(params.toJSONString(), InspectionPlanModel.class);
            // 组装信息
            TaskResultDTO dto = new TaskResultDTO();
            dto.setTaskId(nextTaskId);
            dto.setComment(comment);
            HashMap<String, Object> map = new HashMap<>();
            JyjcInspectionApplication application = inspectionApplicationService.getById(applicationSeq);
            // 需要再确认
            dto.setNextExecuteUserCompanyCode(application.getInspectionUnitCode());
            map.put("approvalStatus", operate);
            // 任务接收人
            map.put("nextTaskAssignee", this.getAssigneeUserId(planModel));
            dto.setResultCode("approvalStatus");
            dto.setVariable(map);// 1-驳回  0-通过
            ProcessTaskDTO complete = cmWorkflowService.completeOrReject(nextTaskId, dto, operate);
            ArrayList<ProcessTaskDTO> processTaskDTOS = new ArrayList<>();
            processTaskDTOS.add(complete);
            List<WorkflowResultDto> resultDto = commonService.buildWorkFlowInfo(processTaskDTOS);
            WorkflowResultDto workflowResultDto = resultDto.get(0);
            JyjcCmTask jyjcCmTask = cmTaskService.getOneBySSeq(String.valueOf(applicationSeq));
            if (workflowResultDto != null && workflowResultDto.getNextTaskId() != null) {
                // 完成待办
                jyjcCmTask.setStatus(this.buildStatus(workflowResultDto.getNextTaskCode(), operate));
                jyjcCmTask.setNextTaskCode(workflowResultDto.getNextTaskCode());
                jyjcCmTask.setNextTaskId(workflowResultDto.getNextTaskId());
                jyjcCmTask.setNextExecuteUserIds(workflowResultDto.getNextExecutorUserIds());
                // 驳回后不能撤回
                jyjcCmTask.setPromoter(OPERATE_PASS.equals(operate) ? RequestContext.getExeUserId() : "");
                jyjcCmTask.setFormData(params.toJSONString());
                cmTaskService.saveOrUpdate(jyjcCmTask);
                updateTaskModel(jyjcCmTask);
                // 创建新的待办
                createdTaskModel(jyjcCmTask, reginParams, application);
            } else {
                // 更新业务信息
                jyjcCmTask.setStatus(FlowStatusEnum.TO_BE_FINISHED.getName());
                jyjcCmTask.setNextTaskCode("");
                jyjcCmTask.setNextTaskId("");
                jyjcCmTask.setPromoter("");
                jyjcCmTask.setNextExecuteUserIds("");
                jyjcCmTask.setFormData(params.toJSONString());
                cmTaskService.updateById(jyjcCmTask);
                LambdaUpdateWrapper<JyjcInspectionApplication> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(BaseEntity::getSequenceNbr, applicationSeq);
                // 通过后将数据回写到业务的字段中，兼容之前的数据格式
                updateWrapper.set(JyjcInspectionApplication::getInspectionChargePerson, planModel.getInspectionChargePerson());
                updateWrapper.set(JyjcInspectionApplication::getRemark2, planModel.getRemark2());
                updateWrapper.set(JyjcInspectionApplication::getPlanData, params.toJSONString());
                inspectionApplicationService.update(updateWrapper);
                this.updatePlanCreateDate(String.valueOf(applicationSeq));
                // 完成待办
                updateTaskModel(jyjcCmTask);
            }
            commonService.saveExecuteFlowData2Redis(instanceId, this.buildInstanceRuntimeData(jyjcCmTask));
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private InstanceRuntimeData buildInstanceRuntimeData(JyjcCmTask jyjcCmTask) {
        return InstanceRuntimeData.builder()
                .nextExecuteUserIds(jyjcCmTask.getNextExecuteUserIds())
                .promoter(jyjcCmTask.getPromoter())
                .nextTaskId(jyjcCmTask.getNextTaskId())
                .build();
    }

    private String buildStatus(String nextTaskCode, String operate) {
        if (nextTaskCode.equals(WorkFlowStatusEnum.INSPECTION_PLAN_ASSIGN_USER.getCode())) {
            return OPERATE_PASS.equals(operate) ? WorkFlowStatusEnum.INSPECTION_PLAN_ASSIGN_USER.getPass() : OPERATE_WITHDRAW.equals(operate) ? WorkFlowStatusEnum.INSPECTION_PLAN_ASSIGN_USER.getRollBack(): WorkFlowStatusEnum.INSPECTION_PLAN_ASSIGN_USER.getReject();
        } else if (nextTaskCode.equals(WorkFlowStatusEnum.INSPECTION_PLAN_INSPECT_CONFIRM.getCode())) {
            return OPERATE_PASS.equals(operate) ? WorkFlowStatusEnum.INSPECTION_PLAN_INSPECT_CONFIRM.getPass() : OPERATE_WITHDRAW.equals(operate) ? WorkFlowStatusEnum.INSPECTION_PLAN_INSPECT_CONFIRM.getRollBack(): WorkFlowStatusEnum.INSPECTION_PLAN_INSPECT_CONFIRM.getReject();
        }
        return "";
    }

    private String getAssigneeUserId(InspectionPlanModel planModel) {
        return planModel.getInspectionChargePerson();
    }

    public boolean withdraw(String instanceId, String nextTaskId, String applicationSeq) {
        String lockKey = CommonServiceImpl.buildJcExecuteLockKey(instanceId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            // 解决并发问题：多个人同时操作一个流程（并发执行通过、驳回、撤回）
            if (!isLocked) {
                throw new BadRequest("当前流程已经被执行！请重新打开页面查看并执行！");
            }
            // 流程执行时，状态及权限校验
            commonService.checkForRevocationFlow(nextTaskId, instanceId);
            ProcessTaskDTO processTaskDTO = cmWorkflowService.rollBack(instanceId);
            List<WorkflowResultDto> workflowResultDtos = taskModelService.buildWorkFlowInfo(Collections.singletonList(processTaskDTO));
            WorkflowResultDto workflowResultDto = workflowResultDtos.get(0);
            JyjcCmTask jyjcCmTask = cmTaskService.getOneBySSeq(applicationSeq);
            jyjcCmTask.setStatus(this.buildStatus(workflowResultDto.getNextTaskCode(), OPERATE_WITHDRAW));
            jyjcCmTask.setNextTaskCode(workflowResultDto.getNextTaskCode());
            jyjcCmTask.setNextTaskId(workflowResultDto.getNextTaskId());
            jyjcCmTask.setNextExecuteUserIds(workflowResultDto.getNextExecutorUserIds());
            jyjcCmTask.setPromoter("");
            cmTaskService.updateById(jyjcCmTask);
            JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(jyjcCmTask));
            jsonObject.put("nextTaskId", workflowResultDto.getNextTaskId());
            jsonObject.put("taskType", BusinessTypeEnum.INSPECTION_PLAN.getCode());
            jsonObject.put("flowStatus", commonService.getDictionaryCodeByName(jyjcCmTask.getStatus()));
            jsonObject.put("flowStatusLabel", jyjcCmTask.getStatus());
            // 赋空 否则后续公共代码报错
            jsonObject.put("nextExecuteUser", "");
            // url参数主键为业务主键，详情查询使用
            jsonObject.put("sequenceNbr", jyjcCmTask.getSSeq());
            jsonObject.remove("formData");
            taskModelService.rollbackTask(instanceId, jsonObject);
            commonService.saveExecuteFlowData2Redis(instanceId, this.buildInstanceRuntimeData(jyjcCmTask));
            return true;
        } catch (InterruptedException e) {
            log.error("撤回流程办理单失败：{}", e.getMessage(), e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.FALSE;
    }

    private void updateTaskModel(JyjcCmTask model) {
        Map<String, Object> params = new HashMap<>();
        params.put("relationId", model.getInstanceId());
        params.put("taskStatus", commonService.getDictionaryCodeByName(model.getStatus()));
        params.put("taskStatusLabel", model.getStatus());
        params.put("flowStatus", commonService.getDictionaryCodeByName(model.getStatus()));
        params.put("flowStatusLabel", model.getStatus());
        JSONObject jsonModel = (JSONObject) JSONObject.toJSON(model);
        taskModelService.removeNoUsedKey(jsonModel);
        TaskMessageDto ta = new TaskMessageDto();
        ta.setInstanceId(model.getInstanceId());
        ta.setStatus(model.getStatus());
        ta.setPromoter(model.getPromoter());
        ta.setSequenceNbr(Long.parseLong(model.getSSeq()));
        ta.setProcessInstanceId(model.getInstanceId());
        ta.setProcessKey(model.getProcessKey());
        ta.setNextTaskId(model.getNextTaskId());
        ta.setBizType(model.getSType());
        params.put("model", ta);
        taskModelService.updateTaskModel(params);
    }


    public Boolean workBenchCreate(String applicationSeq) {
        JyjcCmTask jyjcCmTask = cmTaskService.getOneBySSeq(applicationSeq);
        if(jyjcCmTask != null && jyjcCmTask.getInstanceId() != null) {
            taskModelService.deleteTaskModel(jyjcCmTask.getInstanceId());
            cmTaskService.removeById(jyjcCmTask.getSequenceNbr());
        }
        JyjcInspectionApplication application = inspectionApplicationService.getById(applicationSeq);
        if(application.getStatus().equals(String.valueOf(FlowStatusEnum.TO_BE_FINISHED.getCode()))){
            log.info("人工创建计划排期待办开始：「{}」", applicationSeq);
            LambdaUpdateWrapper<JyjcInspectionApplication> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(BaseEntity::getSequenceNbr, applicationSeq);
            updateWrapper.set(JyjcInspectionApplication::getInspectionChargePerson, null);
            updateWrapper.set(JyjcInspectionApplication::getPlanData, null);
            inspectionApplicationService.update(updateWrapper);
            workBenchTaskCreateService.createOneTask(application);
            log.info("人工进行创建计划排期待办成功：「{}」", applicationSeq);
        } else {
            log.warn("人工进行创建计划排期待办失败,非法的计划状态：「{}」", application.getStatus());
        }
        return true;
    }
}
