package cn.iocoder.yudao.module.crm.service.crmcontractreceivables;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CheckInfoVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontractreceivables.vo.CrmContractReceivablesPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontractreceivables.vo.CrmContractReceivablesRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontractreceivables.vo.CrmContractReceivablesSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontractreceivables.CrmContractReceivablesDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmflow.CrmFlowDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmflow.CrmFlowStepDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmflowlog.CrmFlowLogDO;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontract.CrmContractMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontractreceivables.CrmContractReceivablesMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcustomer.CrmCustomerMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmflow.CrmFlowMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmflow.CrmFlowStepMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmflowlog.CrmFlowLogMapper;
import cn.iocoder.yudao.module.crm.service.crmoperatelog.CrmOperatelogService;
import cn.iocoder.yudao.module.crm.service.userlivetree.UserLiveTreeService;
import cn.iocoder.yudao.module.hrm.enums.ContractStatusEnum;
import cn.iocoder.yudao.module.hrm.enums.FlowStepEnum;
import cn.iocoder.yudao.module.hrm.enums.RelationEnum;
import cn.iocoder.yudao.module.hrm.enums.TypesEnum;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserApiDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserApiVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.CONTRACT_RECEIVABLES_NOT_EXISTS;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.FLOW_NOT_EXISTS;

/**
 * 回款 Service 实现类
 *
 * @author yshop
 */
@Service
@Validated
@Slf4j
public class CrmContractReceivablesServiceImpl implements CrmContractReceivablesService {

    @Resource
    private CrmContractReceivablesMapper contractReceivablesMapper;
    @Resource
    private CrmContractMapper contractMapper;
    @Resource
    private CrmOperatelogService crmOperatelogService;
    @Resource
    private CrmFlowMapper flowMapper;
    @Resource
    private CrmFlowStepMapper flowStepMapper;
    @Resource
    private CrmCustomerMapper customerMapper;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private CrmFlowLogMapper crmFlowLogMapper;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private UserLiveTreeService userLiveTreeService;


    private static final String LOCK_KEY = "receivables:check:lock";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createContractReceivables(CrmContractReceivablesSaveReqVO createReqVO) {
        // 插入
        CrmContractReceivablesDO contractReceivables = BeanUtils.toBean(createReqVO, CrmContractReceivablesDO.class);
        contractReceivables.setOwnerUserId(SecurityFrameworkUtils.getLoginUserId());
        //合同
        CrmContractDO crmContractDO = contractMapper.selectById(createReqVO.getContractId());
        contractReceivables.setOrderAdminId(crmContractDO.getOrderAdminId());

        contractReceivablesMapper.insert(contractReceivables);

        //更新审核
        updateAfter(contractReceivables.getId());


        //插入日志
        crmOperatelogService.createLog("添加回款", 0L, 0L, contractReceivables.getId());
        // 返回
        return contractReceivables.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateContractReceivables(CrmContractReceivablesSaveReqVO updateReqVO) {
        // 校验存在
        validateContractReceivablesExists(updateReqVO.getId());
        // 更新
        CrmContractReceivablesDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractReceivablesDO.class);
        updateObj.setOwnerUserId(SecurityFrameworkUtils.getLoginUserId());
        //合同
        CrmContractDO crmContractDO = contractMapper.selectById(updateObj.getContractId());
        updateObj.setOrderAdminId(crmContractDO.getOrderAdminId());
        contractReceivablesMapper.updateById(updateObj);

        //更新审核
        updateAfter(updateObj.getId());
    }

    @Override
    public void deleteContractReceivables(Long id) {
        // 校验存在
        CrmContractReceivablesDO crmContractReceivablesDO = validateContractReceivablesExists(id);
        if (!ContractStatusEnum.STATUS_0.getValue().equals(crmContractReceivablesDO.getCheckStatus())) {
            throw exception(new ErrorCode(202410102, "回款不是待审核状态不可以删除"));
        }
        // 删除
        contractReceivablesMapper.deleteById(id);
    }

    public void updateAfter(Long id) {
        CrmFlowDO crmFlowDO = flowMapper.selectOne(new LambdaQueryWrapper<CrmFlowDO>()
                .eq(CrmFlowDO::getTypes, TypesEnum.RECEIVABLES.getValue())
                .orderByDesc(CrmFlowDO::getId)
                .last("limit 1"));
        CrmFlowStepDO crmFlowStepDO = flowStepMapper.selectOne(new LambdaQueryWrapper<CrmFlowStepDO>()
                .eq(CrmFlowStepDO::getFlowId, crmFlowDO.getId())
                .orderByAsc(CrmFlowStepDO::getId)
                .last("limit 1"));

        contractReceivablesMapper.updateById(CrmContractReceivablesDO.builder()
                .id(id)
                .stepId(crmFlowStepDO.getId())
                .flowId(crmFlowDO.getId()).build());
    }

    private CrmContractReceivablesDO validateContractReceivablesExists(Long id) {
        CrmContractReceivablesDO crmContractReceivablesDO = contractReceivablesMapper.selectById(id);
        if (crmContractReceivablesDO == null) {
            throw exception(CONTRACT_RECEIVABLES_NOT_EXISTS);
        }

        return crmContractReceivablesDO;
    }

    @Override
    public CrmContractReceivablesRespVO getContractReceivables(Long id) {
        CrmContractReceivablesDO receivablesDO = contractReceivablesMapper.selectById(id);
        CrmContractReceivablesRespVO receivablesRespVO = BeanUtils.toBean(receivablesDO, CrmContractReceivablesRespVO.class);
        receivablesRespVO.setFlowAdminId2(receivablesDO.getFlowAdminId());
        CrmCustomerDO customerDO = customerMapper.selectById(receivablesDO.getCustomerId());
        receivablesRespVO.setCustomerName(customerDO.getName());
        CrmContractDO crmContractDO = contractMapper.selectById(receivablesDO.getContractId());
        receivablesRespVO.setContractName(crmContractDO.getName());
        return receivablesRespVO;
    }

    @Override
    public PageResult<CrmContractReceivablesRespVO> getContractReceivablesPage(CrmContractReceivablesPageReqVO pageReqVO) {
        List<Long> ids = new ArrayList<>();
        Long loginAdminId = SecurityFrameworkUtils.getLoginUserId();
        if (RelationEnum.MY.getValue().equals(pageReqVO.getRelation())) {
            ids.add(loginAdminId);
        } else if (RelationEnum.SUB.getValue().equals(pageReqVO.getRelation())) {
            ids = userLiveTreeService.getItemIdsByUserId(loginAdminId);
        }
        if (CollUtil.isEmpty(ids)){
            return PageResult.empty();
        }
        pageReqVO.setLoginAdminId(loginAdminId);
        IPage mpPage = MyBatisUtils.buildPage(pageReqVO);
        IPage<CrmContractReceivablesRespVO> pageResult = contractReceivablesMapper.selectPageList(mpPage, pageReqVO, ids);
        for (CrmContractReceivablesRespVO respVO : pageResult.getRecords()) {
            List<String> adminIds = StrUtil.split(respVO.getFlowAdminId(), ",");
            if (adminIds.contains(loginAdminId.toString())) {
                respVO.setIsCheck(true);
            } else {
                respVO.setIsCheck(false);
            }
        }
        return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
    }

    @Override
    public String getCode() {
        CrmContractReceivablesDO crmContractReceivablesDO = contractReceivablesMapper
                .selectOne(new LambdaQueryWrapper<CrmContractReceivablesDO>()
                        .orderByDesc(CrmContractReceivablesDO::getNumber).last("limit 1"));
        String format = DateUtil.format(new Date(), "yyyyMMdd");
        if (crmContractReceivablesDO == null) {
            return "HK" + format + "1000";
        }
        List<String> numbers = ReUtil.findAll(PatternPool.NUMBERS, crmContractReceivablesDO.getNumber(), 0);
        String number = format + "1000";
        if (!numbers.isEmpty()) {
            number = numbers.get(0);
        }
        BigDecimal newNumber = new BigDecimal(number).add(BigDecimal.ONE);
        return "HK" + newNumber;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void check(CheckInfoVO checkInfoVO) {
        CrmContractReceivablesDO receivablesDO = contractReceivablesMapper.selectById(checkInfoVO.getId());
        if (receivablesDO == null) {
            throw exception(CONTRACT_RECEIVABLES_NOT_EXISTS);
        }
        CrmFlowDO crmFlowDO = flowMapper.selectById(receivablesDO.getFlowId());
        if (crmFlowDO == null) {
            throw exception(FLOW_NOT_EXISTS);
        }
        Long loginAdminId = SecurityFrameworkUtils.getLoginUserId();
        if (!receivablesDO.getFlowAdminId().contains(loginAdminId)) {
            throw exception(new ErrorCode(202408151, "无权限审核"));
        }
        if (receivablesDO.getCheckStatus() >= ContractStatusEnum.STATUS_2.getValue()) {
            throw exception(new ErrorCode(202408150, "已经审核过"));
        }

        //组合审批日志
        CrmFlowLogDO crmFlowLogDO = CrmFlowLogDO.builder()
                .flowId(receivablesDO.getFlowId())
                .types(TypesEnum.RECEIVABLES.getValue())
                .typesId(receivablesDO.getId())
                .adminId(SecurityFrameworkUtils.getLoginUserId())
                .nickname(SecurityFrameworkUtils.getLoginUserNickname())
                .build();

        RLock lock = redissonClient.getLock(LOCK_KEY);
        if (lock.tryLock()) {
            //加锁防止并发审核
            try {
                //查询审核步骤
                Long count = flowStepMapper.selectCount(new LambdaQueryWrapper<CrmFlowStepDO>()
                        .eq(CrmFlowStepDO::getFlowId, receivablesDO.getFlowId()));
                //加入已经审核人
                String checkAdminId = StrUtil.isEmpty(receivablesDO.getCheckAdminId()) ? loginAdminId.toString() : receivablesDO.getCheckAdminId() + "," + loginAdminId;
                receivablesDO.setCheckAdminId(checkAdminId);
                if (ShopCommonEnum.AGREE_1.getValue().equals(checkInfoVO.getAgreeType())) {
                    //查询当前合同处于那一步骤
                    CrmFlowStepDO crmFlowStepDO = flowStepMapper.selectById(receivablesDO.getStepId());
                    if (crmFlowStepDO.getRelation() < count) {
                        receivablesDO.setCheckStatus(ContractStatusEnum.STATUS_1.getValue());
                        //判断当前步骤是不是最后一个人,如果是 开始审核后开始存储下一步信息
                        if (receivablesDO.getFlowAdminId().size() == 1) {
                            List<Long> list = new ArrayList<>();
                            CrmFlowStepDO next = flowStepMapper.selectOne(new LambdaQueryWrapper<CrmFlowStepDO>()
                                    .eq(CrmFlowStepDO::getFlowId, receivablesDO.getFlowId()).eq(CrmFlowStepDO::getRelation, crmFlowStepDO.getRelation() + 1));
                            if (FlowStepEnum.TYPE_3.getValue().equals(next.getType())) {
                                List<AdminUserApiVO> adminUserDOS = adminUserApi.getUserList(new AdminUserApiDTO().setDeptId(next.getAdminIds()).setNextDept(1)).getCheckedData();
                                list = adminUserDOS.stream().map(AdminUserApiVO::getId).collect(Collectors.toList());
                            } else {
                                list.add(next.getAdminIds());
                            }
                            receivablesDO.setFlowAdminId(list);
                            receivablesDO.setStepId(next.getId());
                        } else {
                            //移除已经审核等人
                            List<Long> nowAdminId = receivablesDO.getFlowAdminId();
                            nowAdminId.remove(loginAdminId);
                            receivablesDO.setFlowAdminId(nowAdminId);
                        }

                    } else {
                        //步骤走完
                        receivablesDO.setCheckStatus(ContractStatusEnum.STATUS_2.getValue());
                        receivablesDO.setFlowAdminId(new ArrayList<>());
                        crmFlowLogDO.setIsEnd(ShopCommonEnum.IS_STATUS_1.getValue());//审核结束
                    }
                    crmFlowLogDO.setStatus(ShopCommonEnum.IS_STATUS_1.getValue());//审核通过
                } else {
                    //拒绝
                    receivablesDO.setCheckStatus(ContractStatusEnum.STATUS_3.getValue());
                    receivablesDO.setCheckAdminId("");
                    receivablesDO.setFlowAdminId(new ArrayList<>());
                    crmFlowLogDO.setRemark(checkInfoVO.getRemark());
                }
                contractReceivablesMapper.updateById(receivablesDO);
                //存入审批日志
                crmFlowLogMapper.insert(crmFlowLogDO);
            } catch (Exception ex) {
                log.error("[message][执行异常]", ex);
            } finally {
                lock.unlock();
            }
        }

    }

}
