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 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());
        updateWrapper.set(JyjcInspectionApplication::getPlanInspectionDate, model.getPlanInspectionDate());
        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);
        }
        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(JSONObject.parseObject(jyjcCmTask.getFormData()));;
        Map<String, Object> applyInfo = inspectionApplicationService.selectBySeq(Long.parseLong(applicationSeq), null);
        applyInfo.remove("planData");
        res.put("applyInfo", applyInfo);
        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("assignee", 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.save(jyjcCmTask);
                updateTaskModel(jyjcCmTask);
                // 创建新的待办
                createdTaskModel(jyjcCmTask, reginParams, application);
                // 更新新的缓存校验数据到redis
            } else {
                // 更新业务信息
                jyjcCmTask.setStatus(FlowStatusEnum.TO_BE_FINISHED.getName());
                jyjcCmTask.setNextTaskCode("");
                jyjcCmTask.setNextTaskId("");
                jyjcCmTask.setNextExecuteUserIds("");
                jyjcCmTask.setFormData(params.toJSONString());
                cmTaskService.updateById(jyjcCmTask);
                LambdaUpdateWrapper<JyjcInspectionApplication> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(BaseEntity::getSequenceNbr, applicationSeq);
                updateWrapper.set(JyjcInspectionApplication::getPlanInspectionDate, planModel.getRemark2());
                // 通过后将数据回写到业务的字段中，兼容之前的数据格式
                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);
            JyjcInspectionApplicationModel inspectionApplicationModel = inspectionApplicationService.queryBySeq(Long.valueOf(applicationSeq));
            if (inspectionApplicationModel == null) {
                log.warn("JyjcInspectionApplicationModel Not Found！sequenceNbr => {}", applicationSeq);
                return false;
            };
            ProcessTaskDTO processTaskDTO = cmWorkflowService.rollBack(inspectionApplicationModel.getProcessInstanceId());
            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", this.getDictNameByCode(jyjcCmTask.getStatus()));
            jsonObject.put("flowStatusLabel", jyjcCmTask.getStatus());
            jsonObject.remove("formData");
            taskModelService.rollbackTask(inspectionApplicationModel.getProcessInstanceId(), 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.getSequenceNbr());
        params.put("taskStatus", model.getStatus());
        params.put("taskStatusLabel", model.getStatus());
        params.put("flowStatus", model.getStatus());
        params.put("flowStatusLabel", model.getStatus());
        JSONObject jsonModel = (JSONObject) JSONObject.toJSON(model);
        taskModelService.removeNoUsedKey(jsonModel);
        Map<String, Object> modelMap = new HashMap<>(jsonModel);
        params.put("model", BeanUtil.toBeanIgnoreError(modelMap, JyjcInspectionApplicationModel.class));
        taskModelService.updateTaskModel(params);
    }



    private String getDictNameByCode(String code) {
        DataDictionary dataDictionary = dataDictionaryService.getByCode(code, "JGGZZZ");
        return dataDictionary != null ? dataDictionary.getName() : "";
    }
}
