package cn.iocoder.yudao.module.bpm.service.business;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.constant.BpmConstant;
import cn.iocoder.yudao.module.bpm.controller.admin.business.vo.BpmApplyRetreatCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.business.vo.BpmBusinessFormCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.elderly.BpmElderlyInfoVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.elderly.ElderlyLogRecordVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmMyPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.business.BpmBusinessFormDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.business.BpmBusinessFormMapper;
import cn.iocoder.yudao.module.bpm.enums.BpmBusinessTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.system.api.bpm.BpmCheckInApi;
import cn.iocoder.yudao.module.system.api.bpm.BpmElderlyApi;
import cn.iocoder.yudao.module.system.api.bpm.BpmElderlyChangeApi;
import cn.iocoder.yudao.module.system.api.bpm.BpmElderlyExpenseApi;
import cn.iocoder.yudao.module.system.api.bpm.vo.ElderlyChangeRecordReqVO;
import cn.iocoder.yudao.module.system.api.bpm.vo.ElderlyRetreatRecordRespVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.ELDERLY_BUSINESS_FORM_NOT_EXISTS;

/**
 * @Author: jinyd
 * @CreateTime: 2024/8/12  10:44
 * @Description: 业务表单实现类
 * @Version: 4.0
 */

@Service
@Validated
public class BpmBusinessFormServiceImpl implements BpmBusinessFormService {

    @Resource
    private BpmBusinessFormMapper bpmBusinessFormMapper;

    @Resource
    @Lazy
    private BpmProcessInstanceApi processInstanceApi;

    @Resource
    private ModelConfigService modelConfigService;

    @Resource
    private BpmElderlyApi bpmElderlyApi;

    @Resource
    private BpmElderlyChangeApi changeApi;

    @Resource
    private BpmCheckInApi checkInApi;

    @Resource
    private BpmElderlyExpenseApi bpmElderlyExpenseApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long create(BpmBusinessFormCreateReqVO createReqVO) {

        // 1. 插入表单

        Integer status = modelConfigService.getModelStatus(String.format(BpmConstant.BPM_PROCESS_KEY_FORMAT,
                BpmBusinessTypeEnum.valueOf(createReqVO.getType()).getKey(),
                createReqVO.getTenantId()));
        // 插入业务表单中间表
        BpmBusinessFormDO businessFormDO = BeanUtils.toBean(createReqVO, BpmBusinessFormDO.class);
        Long userId = getLoginUserId();
        LocalDate nowLocalDate = LocalDate.now();
        if (status == 1) {
            // 修改为系统审批状态
            businessFormDO.setUserId(userId).setStatus(5);
            businessFormDO.setReadStatus(1);
            Long startTenantId = createReqVO.getTenantId();
            businessFormDO.setStartTenantId(startTenantId);
            bpmBusinessFormMapper.insert(businessFormDO);
            // 审批通过后的逻辑
            switch (BpmBusinessTypeEnum.valueOf(createReqVO.getType())) {
                case APPLY_ACCOMMODATION:
                    bpmElderlyApi.generateAdmissionAssessment(businessFormDO.getBusinessId2(), startTenantId);
                    break;

                case APPLY_RETREAT:
//                    bpmElderlyApi.releaseBed(businessFormDO.getBusinessId());
//                    bpmElderlyExpenseApi.generateRefundSettlementOrder(businessFormDO.getBusinessId(), startTenantId);

                    completeApplyRetreat(businessFormDO);
                    break;

                case OUTBOUND_REGISTRATION:

                    break;

                case BED_CHANGE:
//                    changeApi.generateBill(businessFormDO.getBusinessId(), 1, startTenantId);
//                    changeApi.convertBed(businessFormDO.getBusinessId());
                    completeBedChange(businessFormDO);
                    break;
                case NURSE_CHANGE:
//                    changeApi.generateBill(businessFormDO.getBusinessId(), 2, startTenantId);
                    completeNurseChange(businessFormDO);

                    break;

                case CATERING_CHANGE:
//                    changeApi.generateBill(businessFormDO.getBusinessId(), 3, startTenantId);
                    completeCateringChange(businessFormDO);
                    break;

                case PRICE_CHANGE:
//                    changeApi.generatePriceChangeBill(businessFormDO.getBusinessId());
                    completePriceChange(businessFormDO);
                    break;

                case TRANSFER_CHANGE:
                    changeApi.updateTransferChange(businessFormDO.getBusinessId());
                    break;

                case CHECK_IN:
                    checkInApi.completeCheckIn(businessFormDO.getBusinessId());
                default:
                    break;
            }
            return businessFormDO.getId();
        }

        businessFormDO.setUserId(userId).setStatus(BpmTaskStatusEnum.RUNNING.getStatus());
        businessFormDO.setStartTenantId(createReqVO.getTenantId());
        businessFormDO.setReadStatus(0);
        bpmBusinessFormMapper.insert(businessFormDO);

        Map<String, Object> variables = new HashMap<>();
        // 发起 BPM 流程
        String processInstanceId = processInstanceApi.createProcessInstance(userId,
                new BpmProcessInstanceCreateReqDTO()
                        .setProcessDefinitionKey(String.format(BpmConstant.BPM_PROCESS_KEY_FORMAT,
                                BpmBusinessTypeEnum.valueOf(createReqVO.getType()).getKey(),
                                createReqVO.getTenantId()))
                        .setBusinessKey(String.valueOf(businessFormDO.getId()))
                        .setStartUserSelectAssignees(createReqVO.getStartUserSelectAssignees())
                        .setVariables(variables));

        // 将工作流的编号，更新到业务表单中
        bpmBusinessFormMapper.updateById(new BpmBusinessFormDO().setId(businessFormDO.getId()).setProcessInstanceId(processInstanceId));
        return businessFormDO.getId();
    }

    @Override
    public Long createBusinessForm(Long userId, BpmApplyRetreatCreateReqVO createReqVO) {
        // 插入
        BpmBusinessFormDO businessFormDO = BeanUtils.toBean(createReqVO, BpmBusinessFormDO.class);
        bpmBusinessFormMapper.insert(businessFormDO);
        // 返回
        return businessFormDO.getId();
    }

    @Override
    @TenantIgnore
    public void updateBusinessForm(BpmBusinessFormDO businessFormDO) {
        // 校验存在
        validateBusinessFormExists(businessFormDO.getId());
        // 更新
        bpmBusinessFormMapper.updateById(businessFormDO);
    }

    private void validateBusinessFormExists(Long id) {
        if (bpmBusinessFormMapper.selectById(id) == null) {
            throw exception(ELDERLY_BUSINESS_FORM_NOT_EXISTS);
        }
    }

    @Override
    public BpmBusinessFormDO getBusinessFormById(Long id) {
        return bpmBusinessFormMapper.selectById(id);
    }

    @Override
    @TenantIgnore
    public BpmBusinessFormDO getBusinessFormByInstanceId(String processInstanceId) {
        return bpmBusinessFormMapper.selectOne(new LambdaQueryWrapperX<BpmBusinessFormDO>().eq(BpmBusinessFormDO::getProcessInstanceId, processInstanceId));
    }

    @Override
    public BpmBusinessFormDO getBusinessFormByBusinessId(String businessId) {
        BpmBusinessFormDO businessFormDO = new BpmBusinessFormDO();
        List<BpmBusinessFormDO> bpmBusinessFormDOS = bpmBusinessFormMapper.selectList(new LambdaQueryWrapperX<BpmBusinessFormDO>().eq(BpmBusinessFormDO::getBusinessId, businessId));
        if (null != bpmBusinessFormDOS) {
            businessFormDO = bpmBusinessFormDOS.get(0);
        }
        return businessFormDO;
    }

    @Override
    @TenantIgnore
    public ElderlyLogRecordVO getElderlyInfoByInstanceId(String processInstanceId) {
        return bpmBusinessFormMapper.getElderlyInfoByInstanceId(processInstanceId);
    }

    @Override
    @TenantIgnore
    public PageResult<BpmElderlyInfoVO> getMyBpmPage(Long loginUserId, BpmMyPageReqVO pageReqVO) {
        Page<BpmElderlyInfoVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<BpmElderlyInfoVO> list = null;
        if (4 == pageReqVO.getQueryType()) {
            list = bpmBusinessFormMapper.selectCopyPage(loginUserId, page, pageReqVO);
        } else {
            list = bpmBusinessFormMapper.selectMyBpmPage(loginUserId, page, pageReqVO);
//            list = bpmBusinessFormMapper.selectMyBpmPagTest(loginUserId, pageReqVO);
        }
        if (5 == pageReqVO.getQueryType() || 6 == pageReqVO.getQueryType()) {
            list = bpmBusinessFormMapper.selectMyMessagePage(loginUserId, page, pageReqVO);
        }

        return new PageResult<>(list, page.getTotal());
    }


    @Override
    public Map<String, BpmElderlyInfoVO> getElderMapByBusinessKey(Set<String> businessKeys) {
        List<BpmElderlyInfoVO> list = bpmBusinessFormMapper.getElderMapByBusinessKey(businessKeys);
        return convertMap(list, BpmElderlyInfoVO::getId);
    }

    @Override
    public Map<String, BpmElderlyInfoVO> getElderMapByProcessInstanceId(Set<String> processInstanceIds) {
        List<BpmElderlyInfoVO> list = bpmBusinessFormMapper.getElderMapByProcessInstanceId(processInstanceIds);
        return convertMap(list, BpmElderlyInfoVO::getId);
    }

    @Override
    @TenantIgnore
    public BpmBusinessFormDO selectOneByProcessInstanceId(String processInstanceId) {
        return bpmBusinessFormMapper.selectOne(new LambdaQueryWrapper<BpmBusinessFormDO>()
                .eq(BpmBusinessFormDO::getProcessInstanceId, processInstanceId));
    }

    @Override
    @TenantIgnore
    public Map<String, BpmTaskRespVO> getTenantMap(Set<String> processInstanceIds) {
        List<BpmTaskRespVO> list = bpmBusinessFormMapper.getTenantNameList(processInstanceIds);
        return convertMap(list, BpmTaskRespVO::getProcessInstanceId);
    }

    @Override
    public Map<String, BpmElderlyInfoVO> getReadMapById(Set<String> ids) {
        List<BpmElderlyInfoVO> list = bpmBusinessFormMapper.getReadList(ids, getLoginUserId());
        return convertMap(list, BpmElderlyInfoVO::getId);
    }

    @Override
    public void completeApplyRetreat(BpmBusinessFormDO businessFormDO) {
        bpmElderlyApi.releaseBed(businessFormDO.getBusinessId());
        bpmElderlyExpenseApi.generateRefundSettlementOrder(businessFormDO.getBusinessId(), businessFormDO.getStartTenantId());
        BpmBusinessFormDO updateDO = new BpmBusinessFormDO();
        updateDO.setId(businessFormDO.getId());
        updateDO.setCompleteStatus(1);
        bpmBusinessFormMapper.updateById(updateDO);
    }

    @Override
    public void completeBedChange(BpmBusinessFormDO businessFormDO) {
        LocalDate nowLocalDate = LocalDate.now();
        ElderlyChangeRecordReqVO bedChangeRecord = changeApi.getChangeRecord(businessFormDO.getBusinessId());
        if (nowLocalDate.isEqual(bedChangeRecord.getChangeDate()) || bedChangeRecord.getChangeDate().isBefore(nowLocalDate)) {
            changeApi.generateBill(businessFormDO.getBusinessId(), 1, businessFormDO.getStartTenantId());
            changeApi.convertBed(businessFormDO.getBusinessId());
            BpmBusinessFormDO updateDO = new BpmBusinessFormDO();
            updateDO.setId(businessFormDO.getId());
            updateDO.setCompleteStatus(1);
            bpmBusinessFormMapper.updateById(updateDO);
        }
    }

    @Override
    public void completeNurseChange(BpmBusinessFormDO businessFormDO) {
        LocalDate nowLocalDate = LocalDate.now();
        ElderlyChangeRecordReqVO nurseChangeRecord = changeApi.getChangeRecord(businessFormDO.getBusinessId());
        if (nowLocalDate.isEqual(nurseChangeRecord.getChangeDate()) || nurseChangeRecord.getChangeDate().isBefore(nowLocalDate)) {
            changeApi.generateBill(businessFormDO.getBusinessId(), 2, businessFormDO.getStartTenantId());
            BpmBusinessFormDO updateDO = new BpmBusinessFormDO();
            updateDO.setId(businessFormDO.getId());
            updateDO.setCompleteStatus(1);
            bpmBusinessFormMapper.updateById(updateDO);
        }
    }

    @Override
    public void completeCateringChange(BpmBusinessFormDO businessFormDO) {
        LocalDate nowLocalDate = LocalDate.now();
        ElderlyChangeRecordReqVO cateringChangeRecord = changeApi.getChangeRecord(businessFormDO.getBusinessId());
        if (nowLocalDate.isEqual(cateringChangeRecord.getChangeDate()) || cateringChangeRecord.getChangeDate().isBefore(nowLocalDate)) {
            changeApi.generateBill(businessFormDO.getBusinessId(), 3, businessFormDO.getStartTenantId());
            BpmBusinessFormDO updateDO = new BpmBusinessFormDO();
            updateDO.setId(businessFormDO.getId());
            updateDO.setCompleteStatus(1);
            bpmBusinessFormMapper.updateById(updateDO);
        }
    }

    @Override
    public void completePriceChange(BpmBusinessFormDO businessFormDO) {
        LocalDate nowLocalDate = LocalDate.now();
        ElderlyChangeRecordReqVO priceChangeRecord = changeApi.getChangeRecord(businessFormDO.getBusinessId());
        if (nowLocalDate.isEqual(priceChangeRecord.getChangeDate()) || priceChangeRecord.getChangeDate().isBefore(nowLocalDate)) {
            changeApi.generatePriceChangeBill(businessFormDO.getBusinessId());
            BpmBusinessFormDO updateDO = new BpmBusinessFormDO();
            updateDO.setId(businessFormDO.getId());
            updateDO.setCompleteStatus(1);
            bpmBusinessFormMapper.updateById(updateDO);
        }
    }

    @Override
    public void updateById(BpmBusinessFormDO updateDO) {
        bpmBusinessFormMapper.updateById(updateDO);
    }
}
