package org.dromara.payment.bill.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.enums.BusRole;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.payment.bill.domain.MerBill;
import org.dromara.payment.bill.domain.MerBillDetail;
import org.dromara.payment.bill.domain.bo.MerBillDetailBo;
import org.dromara.payment.bill.domain.vo.MerBillDetailVo;
import org.dromara.payment.bill.mapper.MerBillDetailMapper;
import org.dromara.payment.bill.mapper.MerBillMapper;
import org.dromara.payment.bill.service.IMerBillDetailService;
import org.dromara.payment.bill.service.IMerBillService;
import org.dromara.payment.common.domain.BusOperLog;
import org.dromara.payment.common.mapper.BusOperLogMapper;
import org.dromara.payment.enums.BusLogType;
import org.dromara.payment.merchant.domain.BusMerchant;
import org.dromara.payment.merchant.mapper.BusMerchantAccountMapper;
import org.dromara.payment.merchant.mapper.BusMerchantMapper;
import org.dromara.payment.task.domain.MerTaskEnroll;
import org.dromara.payment.task.mapper.MerTaskEnrollMapper;
import org.dromara.payment.util.MoneyUtils;
import org.dromara.payment.worker.domain.UserWorkerAccount;
import org.dromara.payment.worker.domain.bo.UserWorkerAccountBo;
import org.dromara.payment.worker.mapper.UserWorkerAccountMapper;
import org.dromara.payment.worker.service.IUserWorkerAccountService;
import org.dromara.system.api.RemoteNoticeService;
import org.dromara.system.api.domain.vo.RemoteSysNoticeVo;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 账单明细Service业务层处理
 *
 * @author LionLi
 * @date 2024-04-08
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class MerBillDetailServiceImpl implements IMerBillDetailService {

    private final MerBillMapper billMapper;

    private final BusMerchantMapper shDao;

    private final MerBillDetailMapper baseMapper;

    private final BusMerchantMapper merchantMapper;

    private final IUserWorkerAccountService userWorkerAccountService;

    private final MerTaskEnrollMapper taskEnrollMapper;

    private final UserWorkerAccountMapper userWorkerAccountMapper;

    private final BusMerchantAccountMapper merchantAccountMapper;


    private final BusOperLogMapper operLogMapper;

    private final StreamBridge streamBridge;


    @DubboReference
    private RemoteNoticeService noticeService;

    /**
     * 查询账单明细
     */
    @Override
    public MerBillDetailVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询账单明细列表
     */
    @Override
    public TableDataInfo<MerBillDetailVo> queryPageList(MerBillDetailBo bo, PageQuery pageQuery) {
        QueryWrapper<MerBillDetail> lqw = buildQueryWrapper(bo);
        Page<MerBillDetailVo> result = baseMapper.queryPageList(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询账单明细列表
     */
    @Override
    public List<MerBillDetailVo> queryList(MerBillDetailBo bo) {
        QueryWrapper<MerBillDetail> lqw = buildQueryWrapper(bo);
        return baseMapper.queryList(lqw);
    }

    private QueryWrapper<MerBillDetail> buildQueryWrapper(MerBillDetailBo bo) {
        Map<String, Object> params = bo.getParams();
        QueryWrapper<MerBillDetail> lqw = new QueryWrapper<>();
        lqw.eq(StringUtils.isNotBlank(bo.getCno()), "t.cno", bo.getCno());
        lqw.eq(StringUtils.isNotBlank(bo.getBusNo()), "t.bus_no", bo.getBusNo());
        lqw.eq(StringUtils.isNotBlank(bo.getBillNo()), "t.bill_no", bo.getBillNo());
        lqw.eq(bo.getBillId() != null, "t.bill_id", bo.getBillId());
        lqw.eq(bo.getWorkerId() != null, "t.worker_id", bo.getWorkerId());
        lqw.like(StringUtils.isNotBlank(bo.getWorkerName()), "t.worker_name", bo.getWorkerName());
        lqw.eq(StringUtils.isNotBlank(bo.getWorkerPhone()), "t.worker_phone", bo.getWorkerPhone());
        lqw.eq(bo.getWorkerEnrollId() != null, "t.worker_enroll_id", bo.getWorkerEnrollId());
        lqw.like(StringUtils.isNotBlank(bo.getCardId()), "t.card_id", bo.getCardId());
        lqw.eq(StringUtils.isNotBlank(bo.getBankType()), "t.bank_type", bo.getBankType());
        lqw.eq(StringUtils.isNotBlank(bo.getBankCard()), "t.bank_card", bo.getBankCard());
        lqw.eq(StringUtils.isNotBlank(bo.getBankCheque()), "t.bank_cheque", bo.getBankCheque());
        lqw.eq(bo.getMoney() != null, "t.money", bo.getMoney());
        lqw.eq(bo.getRaise() != null, "t.raise", bo.getRaise());
        lqw.eq(bo.getAmount() != null, "t.amount", bo.getAmount());
        lqw.eq(bo.getPayStatus() != null, "t.pay_status", bo.getPayStatus());
        lqw.eq(bo.getPayTime() != null, "t.pay_time", bo.getPayTime());
        lqw.eq(StringUtils.isNotBlank(bo.getPayRemark()), "t.pay_remark", bo.getPayRemark());
        lqw.eq(StringUtils.isNotBlank(bo.getPayReceipt()), "t.pay_receipt", bo.getPayReceipt());
        lqw.eq(bo.getServiceChargeMoney() != null, "t.service_charge_money", bo.getServiceChargeMoney());
        lqw.eq(bo.getAddTime() != null, "t.add_time", bo.getAddTime());
        lqw.eq(StringUtils.isNotBlank(bo.getBankNo()), "t.bank_no", bo.getBankNo());
        lqw.eq(bo.getAgentMoney() != null, "t.agent_money", bo.getAgentMoney());
        lqw.eq(bo.getIsRefund() != null, "t.is_refund", bo.getIsRefund());
        lqw.eq(bo.getRefundTime() != null, "t.refund_time", bo.getRefundTime());
        lqw.eq(StringUtils.isNotBlank(bo.getOutNo()), "t.out_no", bo.getOutNo());
        lqw.eq(bo.getIsOut() != null, "t.is_out", bo.getIsOut());
        lqw.eq(StringUtils.isNotBlank(bo.getPassage()), "t.passage", bo.getPassage());
        lqw.eq(bo.getTaskId() != null, "t.task_id", bo.getTaskId());
        lqw.eq(StringUtils.isNotBlank(bo.getTaskNo()), "t.task_no", bo.getTaskNo());
        lqw.eq(bo.getSydId() != null, "t.syd_id", bo.getSydId());
        lqw.eq(StringUtils.isNotBlank(bo.getSydNo()), "t.syd_no", bo.getSydNo());
        lqw.eq(StringUtils.isNotBlank(bo.getOpNo()), "t.op_no", bo.getOpNo());
        lqw.eq(bo.getOpId() != null, "t.op_id", bo.getOpId());
        lqw.eq(bo.getChannelId() != null, "t.channel_id", bo.getChannelId());
        lqw.eq(StringUtils.isNotBlank(bo.getChannelNo()), "t.channel_no", bo.getChannelNo());
        lqw.eq(StringUtils.isNotBlank(bo.getChannelCode()), "t.channel_code", bo.getChannelCode());
        lqw.eq(bo.getMerId() != null, "t.mer_id", bo.getMerId());
        lqw.eq(StringUtils.isNotBlank(bo.getMerNo()), "t.mer_no", bo.getMerNo());
        lqw.eq(bo.getBusType() != null, "t.bus_type", bo.getBusType());
        lqw.eq(StringUtils.isNotBlank(bo.getPayReceiptB2b()), "t.pay_receipt_b2b", bo.getPayReceiptB2b());
        lqw.eq(StringUtils.isNotBlank(bo.getProNo()), "t.pro_no", bo.getProNo());
        lqw.eq(bo.getChargeMode() != null, "t.charge_mode", bo.getChargeMode());
        lqw.eq(bo.getOpServiceChargeRate() != null, "t.op_service_charge_rate", bo.getOpServiceChargeRate());
        lqw.eq(bo.getOpServiceChargeMoney() != null, "t.op_service_charge_money", bo.getOpServiceChargeMoney());
        lqw.eq(bo.getFhzxServiceChargeRate() != null, "t.fhzx_service_charge_rate", bo.getFhzxServiceChargeRate());
        lqw.eq(bo.getFhzxServiceChargeMoney() != null, "t.fhzx_service_charge_money", bo.getFhzxServiceChargeMoney());
        lqw.eq(bo.getFhzxChannelId() != null, "t.fhzx_channel_id", bo.getFhzxChannelId());
        lqw.eq(bo.getZjServiceChargeRate() != null, "t.zj_service_charge_rate", bo.getZjServiceChargeRate());
        lqw.eq(bo.getZjServiceChargeMoney() != null, "t.zj_service_charge_money", bo.getZjServiceChargeMoney());
        lqw.eq(bo.getZjChannelId() != null, "t.zj_channel_id", bo.getZjChannelId());
        lqw.eq(bo.getYwyServiceChargeRate() != null, "t.ywy_service_charge_rate", bo.getYwyServiceChargeRate());
        lqw.eq(bo.getYwyServiceChargeMoney() != null, "t.ywy_service_charge_money", bo.getYwyServiceChargeMoney());
        lqw.eq(bo.getYwyChannelId() != null, "t.ywy_channel_id", bo.getYwyChannelId());
        lqw.eq(bo.getChannelHalveStatus() != null, "t.channel_halve_status", bo.getChannelHalveStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getBillRemark()), "t.bill_remark", bo.getBillRemark());
        lqw.in(StringUtils.isNotBlank(bo.getPayStatuStrs()),"t.pay_status",StringUtils.splitTo(bo.getPayStatuStrs(), Convert::toInt));
        lqw.in(StringUtils.isNotBlank(bo.getBillIds()),"t.bill_id",StringUtils.splitTo(bo.getBillIds(), Convert::toLong));
        lqw.in(StringUtils.isNotBlank(params.get("ids")),"t.id",StringUtils.splitTo((String)params.get("ids"), Convert::toLong));
        if(StringUtils.isNotBlank(params.get("begin_date")) && StringUtils.isNotBlank(params.get("end_date"))){
            lqw.between("t.add_time",bo.getParams().get("begin_date")+" 00:00:00",bo.getParams().get("end_date")+" 23:59:59");
        }
        if(StringUtils.isNotBlank(params.get("syd_name"))){
            lqw.like("t2.gsmc",params.get("syd_name"));
        }
        if(StringUtils.isNotBlank(params.get("mer_name"))){
            lqw.like("t1.`name`",params.get("mer_name"));
        }
        if(StringUtils.isNotBlank(params.get("channel_no"))){
            lqw.likeRight("t.channel_no",params.get("channel_no"));
        }
        if(StringUtils.isNotBlank(params.get("op_name"))){
            lqw.like("t3.name",params.get("op_name"));
        }
        if(StringUtils.isNotBlank(params.get("fhzx_channel_name"))){
            lqw.like("t4.name",params.get("fhzx_channel_name"));
        }
        if(StringUtils.isNotBlank(params.get("zj_channel_name"))){
            lqw.like("t5.name",params.get("zj_channel_name"));
        }
        if(StringUtils.isNotBlank(params.get("ywy_channel_name"))){
            lqw.like("t6.name",params.get("ywy_channel_name"));
        }
        if(StringUtils.isNotBlank(params.get("fhzx_channel_no"))){
            lqw.eq("t4.no",params.get("fhzx_channel_no"));
        }
        if(StringUtils.isNotBlank(params.get("zj_channel_no"))){
            lqw.eq("t5.no",params.get("zj_channel_no"));
        }
        if(StringUtils.isNotBlank(params.get("ywy_channel_no"))){
            lqw.eq("t6.no",params.get("ywy_channel_no"));
        }

        lqw.orderByDesc("t.add_time");
        return lqw;
    }

    /**
     * 新增账单明细
     */
    @Override
    public Boolean insertByBo(MerBillDetailBo bo) {
        MerBillDetail add = MapstructUtils.convert(bo, MerBillDetail.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改账单明细
     */
    @Override
    public Boolean updateByBo(MerBillDetailBo bo) {
        MerBillDetail update = MapstructUtils.convert(bo, MerBillDetail.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(MerBillDetail entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除账单明细
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 退账单明细
     * @author 张涛
     * @date 2022年3月3日
     * @param busNo 账单明细编号
     * @param trxamt 交易金额
     * @param reason 原因
     * @return 是否退账单明细
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean refundBillDetail(String busNo,Long trxamt,String reason) {
        MerBillDetail billDetail = baseMapper.selectOne(new LambdaQueryWrapper<MerBillDetail>().eq(MerBillDetail::getBusNo,busNo));
        if(billDetail != null && billDetail.getAmount().equals(trxamt)) {
            if(billDetail.getPayStatus() != 1 && billDetail.getPayStatus() != 2) {
                throw new ServiceException("订单["+busNo+"]处理中，退款操作["+reason+"]延后处理");
            }
            if(billDetail.getPayStatus() == 1 && billDetail.getIsRefund() != 1) {//只退票已成功的
                //不用退服务费
                failBillDetail(billDetail.getBillId(),billDetail.getId(),reason,1,null);
            }
            return true;
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean failBillDetail(long billId,Long id,String reason,int isRefund,Long isOut) {
        if(id != null) {
            MerBillDetail billDetail = baseMapper.selectById(id);
            if(billDetail.getPayStatus() != 3){
                return false;
            }
            baseMapper.update(new LambdaUpdateWrapper<MerBillDetail>()
                .eq(MerBillDetail::getId, id)
                .eq(MerBillDetail::getPayStatus, isRefund == 1 ? 1 : 3)
                .set(MerBillDetail::getPayStatus, 2)
                .set(MerBillDetail::getIsRefund,isRefund)
                .set(MerBillDetail::getPayRemark, reason)
                .set(MerBillDetail::getPayTime, new Date()));
            if(billDetail.getWorkerEnrollId() != null && billDetail.getWorkerEnrollId() > 0) {
                //如果是退款，则从成功转到待结算，如果是直接失败，则从结算中到待结算
                taskEnrollMapper.update(new LambdaUpdateWrapper<MerTaskEnroll>().eq(MerTaskEnroll::getId,billDetail.getWorkerEnrollId())
                    .eq(MerTaskEnroll::getSettlementStatus,isRefund == 1 ? 1 : 2)
                    .set(MerTaskEnroll::getSettlementStatus,0));//待结算
            }
        }else {
            baseMapper.update(new LambdaUpdateWrapper<MerBillDetail>()
                .eq(MerBillDetail::getBillId, billId)
                .eq(MerBillDetail::getPayStatus, isRefund == 1 ? 1 : 3)
                .set(MerBillDetail::getPayStatus, 2)
                .set(MerBillDetail::getIsRefund,isRefund)
                .set(MerBillDetail::getPayRemark, reason)
                .set(MerBillDetail::getPayTime, new Date()));
            taskEnrollMapper.updateSettlementStatusByBillId(billId,isRefund == 1 ? 1 : 2,0);
        }
        billResult(billId);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int rePay(MerBillDetailBo bo) {
        MerBillDetailVo merBillDetailVo = this.baseMapper.selectVoById(bo.getId());
        if(merBillDetailVo == null){
            throw new ServiceException("账单明细ID无效");
        }

        if(merBillDetailVo.getPayStatus() != 2){
            throw new ServiceException("账单明细状态不正确,不能进行补发!");
        }
        SpringUtils.getBean(IMerBillService.class).checkPayPassword(BusRole.MERCHANT, merBillDetailVo.getMerId(), () -> {
            BusMerchant merchant = merchantMapper.selectOne(new LambdaQueryWrapper<BusMerchant>().eq(BusMerchant::getId,merBillDetailVo.getMerId()));
            if(StringUtils.isBlank(merchant.getPayPassword())){
                throw new ServiceException("请前往系统管理进行支付密码重置！");
            }
            return !bo.getPassword().equals(merchant.getPayPassword());
        });

        log.info("补发账单原始数据:"+ JsonUtils.toJsonString(merBillDetailVo));
        log.info("补发账单修改数据:"+ JsonUtils.toJsonString(bo));


        bo.setPayStatus(3); //支付中

        UserWorkerAccountBo uwab = new UserWorkerAccountBo();
        uwab.setWorkId(merBillDetailVo.getWorkerId());
        uwab.setPassage(merBillDetailVo.getPassage());
        uwab.setBankCard(bo.getBankCard());
        uwab.setBankType(bo.getBankType());
        uwab.setPhone(merBillDetailVo.getWorkerPhone());

        UserWorkerAccount uwa = this.userWorkerAccountService.rePayMdyWorkerBank(uwab);
        if(uwa == null){
            throw new ServiceException("添加用户银行账户错误!");
        }
        bo.setBankAccId(uwa.getAccId().intValue());
        String[] mns = merBillDetailVo.getMerNo().split("-");
        bo.setBusNo("T"+mns[mns.length - 1]+IdUtil.getSnowflakeNextIdStr());
        MerBillDetail update = MapstructUtils.convert(bo, MerBillDetail.class);
        this.baseMapper.updateById(update);
        operLogMapper.insert(new BusOperLog("商户提交补发出款指令,明细ID:"+bo.getId(), BusRole.MERCHANT,merBillDetailVo.getBillId(),BusLogType.BILL.getId()));
        JSONObject queueParam = new JSONObject();
        queueParam.put("billDetailId", bo.getId());
        streamBridge.send("bill-out-0", MessageBuilder.withPayload(queueParam).build());
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void billResult(long billId) {
        MerBillDetail where = new MerBillDetail();
        where.setBillId(billId);
        Long status = 3l;//账单状态（0:初始状态，1:待支付 2:支付中 3:已支付：4：已取消）
        boolean existsSuccess = false;//是否存在成功的
        boolean existsFail = false;//是否存在失败的
        List<MerBillDetail> details = baseMapper.selectList(new LambdaQueryWrapper<MerBillDetail>().eq(MerBillDetail::getBillId,billId).in(MerBillDetail::getPayStatus,0,1,2,3));
        for (MerBillDetail d : details) {
            if (d.getPayStatus() == 0 || d.getPayStatus() == 3) {
                status = null;
                break;
            }
            if (d.getPayStatus() == 1) {
                existsSuccess = true;
            }
            if (d.getPayStatus() == 2) {
                existsFail = true;
            }
        }
        if (status != null) {
            MerBill bill = billMapper.selectById(billId);
            MerBill updBill = new MerBill();
            updBill.setId(billId);
            updBill.setStatus(status);
            //支付结果（0:初始状态，1:全部成功 2:全部失败 3:部分失败）
            if (existsSuccess && existsFail) {
                updBill.setPayStatus(3l);
            }else if (!existsSuccess && existsFail) {
                updBill.setPayStatus(2l);
            }else if (existsSuccess && !existsFail) {
                updBill.setPayStatus(1l);
            }
            if(bill.getPayTime() == null) {
                updBill.setPayTime(new Date());
            }
            billMapper.updateById(updBill);
            if(bill.getPayTime() == null) {
                BusMerchant merchant = shDao.selectById(bill.getMerId());
                String msg = String.format("%s商户在%s项目%s批次进行企业代付，支付金额%s元", merchant.getName(), bill.getTaskNo(), bill.getNo(), MoneyUtils.getMoneyFormatYuan(bill.getAmountEnt()));
                if (existsFail) {
                    msg = msg + "，存在失败的";
                }
                try{
                    noticeService.addNotice(new RemoteSysNoticeVo("批次代发通知", msg, bill.getMerId().intValue(), BusRole.MERCHANT));
                    noticeService.addNotice(new RemoteSysNoticeVo("批次代发通知", msg, bill.getSydId().intValue(), BusRole.SERVICE));
                }catch(Exception e){
                  e.printStackTrace();
                }
            }
            if(updBill.getPayStatus() == 1) {//全部成功
                merchantAccountMapper.updateInvoiceSettlement(bill.getSydId(), bill.getMerId(), bill.getAmountEnt(), bill.getPassage());
            }
        }
    }
}
