package cn.iocoder.yudao.module.system.api.bpm;

import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.corp.CorpMsgUtil;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.ReflectUtils;
import cn.iocoder.yudao.framework.common.util.string.StringUtil;
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.enums.message.BpmMessageEnum;
import cn.iocoder.yudao.module.system.api.bpm.vo.*;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.yudao.module.system.api.notify.convert.message.UserMessageConvert;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.wechat.dto.CorpSendMessage;
import cn.iocoder.yudao.module.system.api.wechat.dto.CorpWechatTextcard;
import cn.iocoder.yudao.module.system.api.wechat.dto.WechatMessageSaveReq;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ElderlyInformationStatistics;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ExpenseSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.RelativesDO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
import cn.iocoder.yudao.module.system.dal.dataobject.sys.BusinessParamsConfigDO;
import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
import cn.iocoder.yudao.module.system.dal.mysql.sys.BusinessParamsConfigMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.enums.NoticeConstants;
import cn.iocoder.yudao.module.system.enums.change.BusinessTypeEnum;
import cn.iocoder.yudao.module.system.enums.change.ChangeTypeEnum;
import cn.iocoder.yudao.module.system.enums.notice.NotieBusinessTypeEnum;
import cn.iocoder.yudao.module.system.enums.notice.NotifyPriorityEnum;
import cn.iocoder.yudao.module.system.enums.notice.NotifyTypeEnum;
import cn.iocoder.yudao.module.system.service.biz.ElderlyContractService;
import cn.iocoder.yudao.module.system.service.biz.ExpenseService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.mzt.logapi.context.LogRecordContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;

import static cn.iocoder.yudao.module.system.enums.NoticeConstants.*;

/**
 * @Author: jinyd
 * @CreateTime: 2024/8/7  10:02
 * @Description: Bpm事件 API接口实现类
 * @Version: 4.0
 */


@Service
@Validated
@Slf4j
public class BpmCheckInApiImpl implements BpmCheckInApi {

    @Autowired
    private ElderlyCheckInRecordMapper recordMapper;

    @Autowired
    private CheckInApplyMapper checkInApplyMapper;

    @Resource
    private ElderlyContractService contractService;

    @Resource
    private ExpenseService expenseService;

    @Resource
    private ElderlyInfoMapper elderlyInfoMapper;

    @Resource
    private BuildBedMapper buildBedMapper;

    @Resource
    private BuildRoomMapper buildRoomMapper;

    @Resource
    private NurseLevelMapper nurseLevelMapper;

    @Resource
    private AdminUserMapper userMapper;
    @Resource
    private BusinessParamsConfigMapper businessParamsConfigMapper;

    @Autowired
    private NotifyMessageSendApi notifyMessageSendApi;

    @Override
    public Boolean checkRepeat(Long elderlyId) {
        Boolean checkResult = null;
        List<ElderlyCheckInRecordDO> list = recordMapper.selectList(new LambdaQueryWrapperX<ElderlyCheckInRecordDO>()
                .eq(ElderlyCheckInRecordDO::getElderId, elderlyId)
                .eq(ElderlyCheckInRecordDO::getCurrentFlag, 0));
        //判断是否有数据
        if (list != null && list.size() > 0) {
            checkResult = Boolean.TRUE;
        } else {
            checkResult = Boolean.FALSE;
        }
        return checkResult;
    }

    @Override
    public Long addCheckInRecord(CheckInCreateReqVO createReqVO) {
        CheckInExpenseSaveReqVO expenseBO = createReqVO.getExpenseBO();
        CheckInContractReqVO elderlyContractDO = createReqVO.getElderlyContractDO();
        // 使用 Gson 将对象转换为 JSON 字符串
        Gson gson = new Gson();
        CheckInApplyDO checkInApplyDO = CheckInApplyDO.builder().bedId(expenseBO.getBedId()).elderlyId(expenseBO.getElderId())
                .totalAmount(null).bedId(expenseBO.getBedId()).totalAmount(expenseBO.getTotalAmount()).remark(expenseBO.getRemark())
                .checkInTime(expenseBO.getCheckInTime())
                .checkInDeadlineTime(elderlyContractDO.getExpireTime())
                .nurseLevelId(expenseBO.getNurseLevelId())
                .isPrivateRoom(expenseBO.getIsPrivateRoom())
                .specialCareNotes(expenseBO.getSpecialCareNotes())
                .contractJson(gson.toJson(elderlyContractDO))
                .monthlyExpensesJson(gson.toJson(expenseBO.getMonthlyExpenses()))
                .oneTimeExpensesJson(gson.toJson(expenseBO.getOneTimeExpenses()))
                .tenantId(createReqVO.getTenantId())
                .stageExpensesJson(gson.toJson(expenseBO.getStageExpenses())).build();
        checkInApplyMapper.insert(checkInApplyDO);
//        elderlyCheckInRecordDO.setAssociateId(checkInApplyDO.getId());
//        elderlyCheckInRecordDO.setElderId(createReqVO.getElderlyId());
//        elderlyCheckInRecordDO.setStatus(0);
//        elderlyCheckInRecordDO.setTenantId(TenantContextHolder.getTenantId());
//        elderlyCheckInRecordDO.setCreatedTime(new Date());
//        elderlyCheckInRecordDO.setFileStatus(0);
//        recordMapper.insert(elderlyCheckInRecordDO);

        ElderlyCheckInRecordDO insertBO = recordMapper.selectOne(new LambdaQueryWrapperX<ElderlyCheckInRecordDO>()
                .eq(ElderlyCheckInRecordDO::getElderId, createReqVO.getElderlyId())
                .orderByDesc(ElderlyCheckInRecordDO::getCreatedTime)
                .last("LIMIT 1"));
        if (null == insertBO) {
            insertBO = new ElderlyCheckInRecordDO();
        }
        insertBO.setId(null);
        insertBO.setCheckInTime(elderlyContractDO.getBeginTime());
        insertBO.setCreatedTime(new Date());
        insertBO.setElderId(createReqVO.getElderlyId());
        insertBO.setStatus(2);
        insertBO.setAssociateId(checkInApplyDO.getId());
        insertBO.setCurrentFlag(0);
        insertBO.setTenantId(createReqVO.getTenantId());
        recordMapper.insert(insertBO);
        return insertBO.getId();
    }

    @Override
    public Integer updateCurrentFlag(Long id) {
        ElderlyCheckInRecordDO elderlyCheckInRecordDO = new ElderlyCheckInRecordDO();
        elderlyCheckInRecordDO.setId(id);
        // 状态为1，表示不是最新的流程
        elderlyCheckInRecordDO.setCurrentFlag(1);
        return recordMapper.updateById(elderlyCheckInRecordDO);
    }

    @Override
    public Integer updateCurrentFlagByElderId(Long elderId) {
        ElderlyCheckInRecordDO elderlyCheckInRecordDO = new ElderlyCheckInRecordDO();
        // 状态为1，表示不是最新的流程
        elderlyCheckInRecordDO.setCurrentFlag(1);
        return recordMapper.update(elderlyCheckInRecordDO, new LambdaQueryWrapperX<ElderlyCheckInRecordDO>().eq(ElderlyCheckInRecordDO::getElderId, elderId));
    }

    @Override
    public Integer delectCheckInRecord(Long id) {
        return recordMapper.deleteById(id);
    }

    @Override
    public Integer completeCheckIn(Long businessId) {
        ElderlyCheckInRecordDO elderlyCheckInRecordDO = recordMapper.selectOneTenantIgnore(businessId);
        CheckInApplyDO checkInApplyDO = checkInApplyMapper.selectOneTenantIgnore(elderlyCheckInRecordDO.getAssociateId());
        String contractJson = checkInApplyDO.getContractJson();
        Gson gson = new Gson();
        CheckInContractReqVO contractReqVO = gson.fromJson(contractJson, CheckInContractReqVO.class);
        CheckInExpenseSaveReqVO checkInExpenseSaveReqVO = BeanUtils.toBean(checkInApplyDO, CheckInExpenseSaveReqVO.class);
        ElderlyContractDO elderlyContractDO = BeanUtils.toBean(contractReqVO, ElderlyContractDO.class);
        elderlyContractDO.setTenantId(elderlyCheckInRecordDO.getTenantId());
        ExpenseSaveReqVO expenseSaveReqVO = BeanUtils.toBean(checkInExpenseSaveReqVO, ExpenseSaveReqVO.class);
        Type listType = new TypeToken<List<ExpenseItemDO>>() {}.getType();
        List<ExpenseItemDO> monthlyExpenses = gson.fromJson(checkInApplyDO.getMonthlyExpensesJson(), listType);
        List<ExpenseItemDO> oneTimeExpenses = gson.fromJson(checkInApplyDO.getOneTimeExpensesJson(), listType);
        List<ExpenseItemDO> stageExpenses = gson.fromJson(checkInApplyDO.getStageExpensesJson(), listType);
        expenseSaveReqVO.setMonthlyExpenses(monthlyExpenses);
        expenseSaveReqVO.setOneTimeExpenses(oneTimeExpenses);
        expenseSaveReqVO.setStageExpenses(stageExpenses);
        expenseSaveReqVO.setElderId(checkInApplyDO.getElderlyId());
        expenseSaveReqVO.setTenantId(elderlyCheckInRecordDO.getTenantId());
        expenseSaveReqVO.setInStatusType(Integer.valueOf(elderlyContractDO.getInStatusType()));
        elderlyContractDO.setAmount(expenseSaveReqVO.getTotalAmount());
        contractService.createContract(elderlyContractDO);
        Long expenseOrderId = expenseService.createExpense(expenseSaveReqVO);

        ElderlyInfoDO elderlyInfoDO = elderlyInfoMapper.selectById(checkInApplyDO.getElderlyId());

        notifyMessageSendApi.updateMessageByCodeAndPkMessage(BpmMessageEnum.CHECK_IN_HANDLE.getTemplateCode(), elderlyInfoDO.getId());

        LocalDate date = elderlyCheckInRecordDO.getCheckInTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        String billingMonth = date.format(DateTimeFormatter.ofPattern("yyyy-MM"));
        Map<String, Object> templateParams = new HashMap<>();
        templateParams.put("elderName", elderlyInfoDO.getElderName());
        templateParams.put("billingMonth", billingMonth);

        List<BusinessParamsConfigDO> businessParamsConfigDOS = businessParamsConfigMapper
                .selectListByName(elderlyCheckInRecordDO.getTenantId(), NoticeConstants.CHECK_IN_HANDLE_NAME);

        String title = CorpMsgUtil.TITLE_TODO;
        CorpSendMessage corpSendMessage = new CorpSendMessage();
        WechatMessageSaveReq wechatMessageSaveReq = new WechatMessageSaveReq();
        CorpWechatTextcard corpTextCard = new CorpWechatTextcard();
        corpTextCard.setTitle(title);
        corpTextCard.setBtntxt(CorpMsgUtil.BTNTXT_DETAIL_NOTICE);
        corpTextCard.setUrl("");
        corpSendMessage.setTextcard(corpTextCard);
        corpSendMessage.setContentType(1);
        wechatMessageSaveReq.setTitle(title);
        wechatMessageSaveReq.setPkMessage(elderlyInfoDO.getId().toString());
        wechatMessageSaveReq.setPkMessage2("");
        wechatMessageSaveReq.setApplyTime(DateUtils.localDateTimeToStr(LocalDateTime.now()));

        if (null == businessParamsConfigDOS || businessParamsConfigDOS.size() == 0) return 0;
        businessParamsConfigDOS.forEach(businessParamsConfigDO -> {
            wechatMessageSaveReq.setTypeName(businessParamsConfigDO.getConfigName());
            corpSendMessage.setWechatMessageSaveReq(wechatMessageSaveReq);
            List<MessgeUserRespVO> userList = new ArrayList<>();
            List<MessgeUserRespVO> deptList = new ArrayList<>();
            if (StringUtil.isNotEmptyORNull(businessParamsConfigDO.getValue())) {
                String[] split = businessParamsConfigDO.getValue().split(",");
                Set<Long> userIds = new HashSet<>();
                for (String s : split) {
                    userIds.add(Long.parseLong(s));
                }
                userList = userMapper.selectMessageUserByIds(userIds, 0);
            }
            if (StringUtil.isNotEmptyORNull(businessParamsConfigDO.getValue2())) {
                String[] split = businessParamsConfigDO.getValue2().split(",");
                Set<Long> deptIds = new HashSet<>();
                for (String s : split) {
                    deptIds.add(Long.parseLong(s));
                }
                deptList = userMapper.selectMessageUserByIds(deptIds, 1);
            }
            List<MessgeUserRespVO> mergeList = new ArrayList<>();
            Map<Long, Long> map = new HashMap<>();
            for (int i = 0; i < userList.size(); i++) {
                MessgeUserRespVO messgeUserRespVO = userList.get(i);
                map.put(messgeUserRespVO.getId(), messgeUserRespVO.getId());
                mergeList.add(messgeUserRespVO);
            }
            for (int i = 0; i < deptList.size(); i++) {
                if (!map.containsKey(deptList.get(i).getId())) {
                    mergeList.add(deptList.get(i));
                }
            }
            String templateCode = "";
            String url = "";
            if (NoticeConstants.CHECK_IN_HANDLE_NAME.equals(businessParamsConfigDO.getConfigName())) {
                // TODO 参数
                url = String.format(EXPENSE_ORDER_HANDLE_URL, expenseOrderId, elderlyCheckInRecordDO.getTenantId());
                templateCode = BpmMessageEnum.EXPENSE_ORDER_HANDLE.getTemplateCode();
            }
            for (int i = 0; i < mergeList.size(); i++) {
                MessgeUserRespVO messgeUserRespVO = mergeList.get(i);
                NotifySendSingleToUserReqDTO checkInHandleReqdto = UserMessageConvert.INSTANCE.messageConvert(messgeUserRespVO.getId(),
                        elderlyInfoDO.getId().toString(), null, url, templateCode, templateParams,
                        NotieBusinessTypeEnum.ORDINARY.getValue(), NotifyTypeEnum.NOTIFY_TODO.getValue(),
                        NotifyPriorityEnum.HIGH.getValue(), 0, messgeUserRespVO.getTenantId());
                corpSendMessage.setTouser(messgeUserRespVO.getCorpUserId());
                wechatMessageSaveReq.setCorpUserId(messgeUserRespVO.getCorpUserId());
                wechatMessageSaveReq.setStartTenantId(messgeUserRespVO.getTenantId());
                wechatMessageSaveReq.setTenantName(messgeUserRespVO.getAbbreviation());
                notifyMessageSendApi.sendMessageAndCorpMessage(checkInHandleReqdto, corpSendMessage);
            }
        });


        return 0;
    }

    @Override
    public PageResult<CheckInRecordRespVO> getProcessInstanceCheckInPage(CheckInApiPageReqVO pageReqVO) {
        Page<CheckInRecordRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        pageReqVO.setType(BusinessTypeEnum.CHECK_IN.getValue());
//        List<CheckInRecordRespVO> list = changeRecordMapper.selectTransferListPage(page, pageReqVO);
        List<CheckInRecordRespVO> list = checkInApplyMapper.selectCheckInListPage(page, pageReqVO);
//        for (CheckInRecordRespVO checkInRecordRespVO : list) {
//            ElderlyInfoDO elderlyInfoDO = elderlyInfoMapper.selectById(checkInRecordRespVO.getElderId());
//            if (elderlyInfoDO != null && elderlyInfoDO.getInStatus() == 2){
//                checkInRecordRespVO.setStatus(3);
//            }
//        }
        return new PageResult<>(list, page.getTotal());

    }

    @Override
    @TenantIgnore
    public Integer bindBedByType(CheckInCreateReqVO createReqVO, Integer queryType) {
        CheckInExpenseSaveReqVO expenseBO = createReqVO.getExpenseBO();

        // 绑定床位信息
        ElderlyInfoDO elderlyInfo = elderlyInfoMapper.selectById(expenseBO.getElderId());
        Long bedId = elderlyInfo.getBedId();
        BuildBedDO bedDO = buildBedMapper.selectById(expenseBO.getBedId());
        // queryType 0:绑定 1:解绑
        if (0 == queryType) {
            if (bedDO != null) {
                elderlyInfo.setRoomId(bedDO.getRoomId());
                elderlyInfo.setFloorId(bedDO.getFloorId());
                elderlyInfo.setBuildId(bedDO.getBuildId());

                bedDO.setStatus(1);
                buildBedMapper.updateById(bedDO);

                BuildRoomDO roomDO = buildRoomMapper.selectById(bedDO.getRoomId());
                // TODO 为什么不在楼栋房间表增加床位数量字段，状态增加满员，更加精确形容房间的状态
                roomDO.setStatus(1);
                buildRoomMapper.updateById(roomDO);
            }
        } else {
            // 解绑之前的床位
            BuildBedDO historyBedDO  = buildBedMapper.selectById(bedId);
            if (null != historyBedDO) {
                // 释放之前的床位
                historyBedDO.setStatus(0);
                buildBedMapper.updateById(historyBedDO);
                BuildRoomDO historyRoomDO = buildRoomMapper.selectById(historyBedDO.getRoomId());
                List<BuildBedDO> buildBedDOS = buildBedMapper.selectList(new LambdaQueryWrapperX<BuildBedDO>()
                        .eq(BuildBedDO::getRoomId, bedDO.getRoomId())
                        .eq(BuildBedDO::getTenantId, createReqVO.getTenantId()));
                if(null != buildBedDOS && buildBedDOS.size() == 1) {
                    historyRoomDO.setStatus(0);
                    buildRoomMapper.updateById(historyRoomDO);
                }
            }
            if (null != bedDO) {
                elderlyInfo.setRoomId(bedDO.getRoomId());
                elderlyInfo.setFloorId(bedDO.getFloorId());
                elderlyInfo.setBuildId(bedDO.getBuildId());

                bedDO.setStatus(1);
                buildBedMapper.updateById(bedDO);
                BuildRoomDO roomDO = buildRoomMapper.selectById(bedDO.getRoomId());
                roomDO.setStatus(1);
                buildRoomMapper.updateById(roomDO);

            }

        }
        elderlyInfo.setInStatus(1);
        elderlyInfo.setBedId(expenseBO.getBedId());
        elderlyInfo.setCheckInTime(expenseBO.getCheckInTime());
        elderlyInfo.setNurseLevelId(expenseBO.getNurseLevelId());
        elderlyInfo.setIsPrivateRoom(expenseBO.getIsPrivateRoom());
        NurseLevelDO nurseLevelDO = nurseLevelMapper.selectById(expenseBO.getNurseLevelId());
        elderlyInfo.setNurseLevelName(nurseLevelDO == null ? null : nurseLevelDO.getNurseLevelName());
        elderlyInfoMapper.updateById(elderlyInfo);
        ElderlyCheckInRecordDO elderlyCheckInRecord = recordMapper.selectOne(new LambdaQueryWrapperX<ElderlyCheckInRecordDO>()
                .eq(ElderlyCheckInRecordDO::getElderId, elderlyInfo.getId())
                .eq(ElderlyCheckInRecordDO::getTenantId, createReqVO.getTenantId())
                .orderByDesc(ElderlyCheckInRecordDO::getCreatedTime)
                .last("LIMIT 1"));
        if (elderlyCheckInRecord != null) {
            elderlyCheckInRecord.setStatus(2); //修改状态为已入住
            elderlyCheckInRecord.setCheckInTime(expenseBO.getCheckInTime());
            recordMapper.updateById(elderlyCheckInRecord);
        }

        return null;
    }
}
