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

import cn.hutool.core.collection.CollUtil;
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.crminvoice.vo.CrmInvoicePageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crminvoice.vo.CrmInvoiceRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crminvoice.vo.CrmInvoiceSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractDO;
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.dataobject.crminvoice.CrmInvoiceDO;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontract.CrmContractMapper;
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.dal.mysql.crminvoice.CrmInvoiceMapper;
import cn.iocoder.yudao.module.crm.service.userlivetree.UserLiveTreeService;
import cn.iocoder.yudao.module.hrm.enums.*;
import cn.iocoder.yudao.module.system.api.mail.MailSendApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
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.List;

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

/**
 * 发票 Service 实现类
 *
 * @author yshop
 */
@Service
@Validated
@Slf4j
public class CrmInvoiceServiceImpl implements CrmInvoiceService {

    @Resource
    private CrmInvoiceMapper invoiceMapper;
    @Resource
    private CrmContractMapper contractMapper;
    @Resource
    private CrmFlowMapper flowMapper;
    @Resource
    private CrmFlowStepMapper flowStepMapper;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private CrmCustomerMapper customerMapper;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private CrmFlowLogMapper crmFlowLogMapper;
    @Resource
    private UserLiveTreeService userLiveTreeService;

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createInvoice(CrmInvoiceSaveReqVO createReqVO) {
        CrmContractDO crmContractDO = preCheck(createReqVO);
        // 插入
        CrmInvoiceDO invoice = BeanUtils.toBean(createReqVO, CrmInvoiceDO.class);
        invoice.setCustomerId(crmContractDO.getCustomerId());
        invoiceMapper.insert(invoice);

        updateAfter(invoice.getId());
        // 返回
        return invoice.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInvoice(CrmInvoiceSaveReqVO updateReqVO) {
        CrmContractDO crmContractDO = preCheck(updateReqVO);
        // 校验存在
        validateInvoiceExists(updateReqVO.getId());
        // 更新
        CrmInvoiceDO updateObj = BeanUtils.toBean(updateReqVO, CrmInvoiceDO.class);
        updateObj.setCustomerId(crmContractDO.getCustomerId());
        invoiceMapper.updateById(updateObj);

        updateAfter(updateReqVO.getId());
    }

    @Override
    public void deleteInvoice(Long id) {
        // 校验存在
        CrmInvoiceDO invoiceDO = validateInvoiceExists(id);
        if (!ContractStatusEnum.STATUS_0.getValue().equals(invoiceDO.getCheckStatus())) {
            throw exception(new ErrorCode(202410103, "发票不是待审核状态不可以删除"));
        }
        // 删除
        invoiceMapper.deleteById(id);
    }

    public CrmContractDO preCheck(CrmInvoiceSaveReqVO createReqVO) {
        CrmContractDO crmContractDO = contractMapper.selectById(createReqVO.getContractId());
        if (crmContractDO == null) {
            throw exception(CONTRACT_NOT_EXISTS);
        }
        if (!ContractStatusEnum.STATUS_2.getValue().equals(crmContractDO.getCheckStatus())) {
            throw exception(new ErrorCode(202406160, "合同未通过审核不可以申请发票"));
        }
        Integer[] checkStatus = {0, 1};
        Long count = invoiceMapper.selectCount(new LambdaQueryWrapper<CrmInvoiceDO>()
                .in(CrmInvoiceDO::getCheckStatus, checkStatus).or().eq(CrmInvoiceDO::getStatus, checkStatus));
        if (createReqVO.getId() == null && count > 0) {
            throw exception(new ErrorCode(202406161, "该合同还有未审核或者未开票等申请，不可以继续申请发票"));
        }
        //判断开票金额
        BigDecimal money = createReqVO.getMoney().add(crmContractDO.getInvoiceMoney());
        if (money.compareTo(crmContractDO.getMoney()) > 0) {
            throw exception(new ErrorCode(202406161, "累积开票金额不能大于合同金额"));
        }

        return crmContractDO;
    }

    public void updateAfter(Long id) {
        CrmFlowDO crmFlowDO = flowMapper.selectOne(new LambdaQueryWrapper<CrmFlowDO>()
                .eq(CrmFlowDO::getTypes, TypesEnum.INVOICE.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"));

        invoiceMapper.updateById(CrmInvoiceDO.builder()
                .id(id)
                .stepId(crmFlowStepDO.getId())
                .flowId(crmFlowDO.getId()).build());
    }

    private CrmInvoiceDO validateInvoiceExists(Long id) {
        CrmInvoiceDO invoiceDO = invoiceMapper.selectById(id);
        if (invoiceDO == null) {
            throw exception(INVOICE_NOT_EXISTS);
        }

        return invoiceDO;
    }

    @Override
    public CrmInvoiceRespVO getInvoice(Long id) {
        CrmInvoiceDO invoiceDO = invoiceMapper.selectById(id);
        CrmInvoiceRespVO invoiceRespVO = BeanUtils.toBean(invoiceDO, CrmInvoiceRespVO.class);
        invoiceRespVO.setFlowAdminId2(invoiceDO.getFlowAdminId());
        CrmCustomerDO customerDO = customerMapper.selectById(invoiceDO.getCustomerId());
        invoiceRespVO.setCustomerName(customerDO.getName());
        CrmContractDO crmContractDO = contractMapper.selectById(invoiceDO.getContractId());
        invoiceRespVO.setContractName(crmContractDO.getName());
        return invoiceRespVO;
    }

    @Override
    public PageResult<CrmInvoiceRespVO> getInvoicePage(CrmInvoicePageReqVO 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();
        }
        PageResult<CrmInvoiceRespVO> pageResult = invoiceMapper.selectPage2(pageReqVO, ids);
        for (CrmInvoiceRespVO respVO : pageResult.getList()) {
            List<String> adminIds = StrUtil.split(respVO.getFlowAdminId(), ",");
            if (adminIds.contains(loginAdminId.toString())) {
                respVO.setIsCheck(true);
            } else {
                respVO.setIsCheck(false);
            }
        }
        return pageResult;
    }


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

        //组合审批日志
        CrmFlowLogDO crmFlowLogDO = CrmFlowLogDO.builder()
                .flowId(invoiceDO.getFlowId())
                .types(TypesEnum.INVOICE.getValue())
                .typesId(invoiceDO.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, invoiceDO.getFlowId()));
                //加入已经审核人
                String checkAdminId = StrUtil.isEmpty(invoiceDO.getCheckAdminId()) ? loginAdminId.toString() : invoiceDO.getCheckAdminId() + "," + loginAdminId;
                invoiceDO.setCheckAdminId(checkAdminId);
                if (ShopCommonEnum.AGREE_1.getValue().equals(checkInfoVO.getAgreeType())) {
                    //查询当前合同处于那一步骤
                    CrmFlowStepDO crmFlowStepDO = flowStepMapper.selectById(invoiceDO.getStepId());
                    if (crmFlowStepDO.getRelation() < count) {
                        invoiceDO.setCheckStatus(ContractStatusEnum.STATUS_1.getValue());
                        //判断当前步骤是不是最后一个人,如果是 开始审核后开始存储下一步信息
                        if (invoiceDO.getFlowAdminId().size() == 1) {
                            List<Long> list = new ArrayList<>();
                            CrmFlowStepDO next = flowStepMapper.selectOne(new LambdaQueryWrapper<CrmFlowStepDO>()
                                    .eq(CrmFlowStepDO::getFlowId, invoiceDO.getFlowId()).eq(CrmFlowStepDO::getRelation, crmFlowStepDO.getRelation() + 1));
                            if (FlowStepEnum.TYPE_3.getValue().equals(next.getType())) {
                                list = userLiveTreeService.getItemIdsByUserId(next.getAdminIds());
                            } else {
                                list.add(next.getAdminIds());
                            }
                            invoiceDO.setFlowAdminId(list);
                            invoiceDO.setStepId(next.getId());
                        } else {
                            //移除已经审核等人
                            List<Long> nowAdminId = invoiceDO.getFlowAdminId();
                            nowAdminId.remove(loginAdminId);
                            invoiceDO.setFlowAdminId(nowAdminId);
                        }

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

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void issueInvoice(CrmInvoiceSaveReqVO updateReqVO) {
        CrmInvoiceDO crmInvoiceDO = invoiceMapper.selectById(updateReqVO.getId());
        if (InvoiceStatusEnum.STATUS_2.getValue().equals(crmInvoiceDO.getStatus())) {
            throw exception(new ErrorCode(202408240, "发票已经开具啦"));
        }
        CrmInvoiceDO updateObj = BeanUtils.toBean(updateReqVO, CrmInvoiceDO.class);
        updateObj.setStatus(InvoiceStatusEnum.STATUS_2.getValue());
        invoiceMapper.updateById(updateObj);

        CrmContractDO crmContractDO = contractMapper.selectById(crmInvoiceDO.getContractId());
        BigDecimal money = crmContractDO.getInvoiceMoney().add(updateObj.getMoney());
        crmContractDO.setInvoiceMoney(money);
        contractMapper.updateById(crmContractDO);

//        //发送邮件
//        if(StrUtil.isNotEmpty(updateObj.getFiles()) && StrUtil.isNotEmpty(updateObj.getEmail())){
//            Map<String, Object> templateParams = new HashMap<>();
//            templateParams.put("files",updateObj.getFiles());
//            mailSendApi.sendSingleMail(new MailSendSingleReqDTO()
//                    .setMail(updateObj.getEmail()).setUserId(updateObj.getCustomerId()).setUserType(UserTypeEnum.CUSTOMER.getValue())
//                    .setTemplateCode("invoice-send").setTemplateParams(templateParams));
//        }
    }
}
