package com.sckw.payment.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.sckw.core.exception.BusinessException;
import com.sckw.core.model.constant.NumberConstant;
import com.sckw.core.model.page.PageRes;
import com.sckw.core.utils.CollectionUtils;
import com.sckw.core.web.context.LoginUserHolder;
import com.sckw.core.web.response.HttpResult;
import com.sckw.manage.api.RemoteManageService;
import com.sckw.payment.api.model.constant.ChannelEnum;
import com.sckw.payment.api.model.dto.common.R;
import com.sckw.payment.dao.*;
import com.sckw.payment.model.*;
import com.sckw.payment.model.dto.wallet.Freeze;
import com.sckw.payment.model.dto.wallet.WithdrawRes;
import com.sckw.payment.model.dto.xw.WithdrawDto;
import com.sckw.payment.model.vo.CashActionEnum;
import com.sckw.payment.model.vo.CashStatusEnum;
import com.sckw.payment.model.vo.PrepayActionEnum;
import com.sckw.payment.model.vo.UserPara;
import com.sckw.payment.model.vo.req.CashVo;
import com.sckw.payment.model.vo.req.CashWalletReq;
import com.sckw.payment.model.vo.req.CreateUserReq;
import com.sckw.payment.model.vo.req.page.ListPage;
import com.sckw.payment.model.vo.req.page.PayPage;
import com.sckw.payment.model.vo.req.page.PrePayPage;
import com.sckw.payment.model.vo.res.ListVo;
import com.sckw.payment.model.vo.res.PayVo;
import com.sckw.payment.model.vo.res.PrepayVo;
import com.sckw.payment.utils.MoneyUtil;
import com.sckw.redis.config.RedisLockUtil;
import com.sckw.redis.constant.RedisConstant;
import com.sckw.system.api.RemoteSystemService;
import com.sckw.system.api.RemoteUserService;
import com.sckw.system.api.model.dto.res.EntCacheResDto;
import com.sckw.system.api.model.dto.res.EntCertificateResDto;
import com.sckw.system.api.model.dto.res.EntTypeResDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 钱包
 *
 * @author xucaiqin
 * @date 2023-08-28 15:38:53
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WalletService {
    private final PayCenterService payCenterService;
    private final PayXwService payXwService;
    private final KwpWalletRelationService walletRelationService;
    private final KwpWalletCashMapper kwpWalletCashMapper;
    private final KwpWalletPrepayMapper kwpWalletPrepayMapper;
    private final KwpWalletSubMapper kwpWalletSubMapper;
    private final KwpWalletPayMapper kwpWalletPayMapper;
    private final KwpWalletMapper kwpWalletMapper;
    private final RedisLockUtil redisLockUtil;
    private final KwpWalletService kwpWalletService;
    @DubboReference(version = "1.0.0", group = "design", check = false)
    private RemoteSystemService remoteSystemService;
    @DubboReference(version = "1.0.0", group = "design", check = false)
    private RemoteManageService remoteManageService;
    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
    private RemoteUserService remoteUserService;


    /**
     * @return
     */
    private String getFirmName(Long entId) {
        EntCacheResDto entCacheResDto = remoteSystemService.queryEntCacheById(entId);
        if (Objects.nonNull(entCacheResDto)) {
            return entCacheResDto.getFirmName();
        }
        return "";
    }


    /**
     * 查询中台用户id
     *
     * @param id
     * @return
     */
    private String getUid(Long id) {
        if (Objects.isNull(id)) {
            return null;
        }
        return walletRelationService.getRelation(id);
    }

    public String userNotice(JSONObject jsonObject) {
        log.info("开户回调 {}", jsonObject.toJSONString());
        if (StrUtil.equals(jsonObject.getString("status"), "true")) {
            JSONObject data = jsonObject.getJSONObject("data");
            if (Objects.nonNull(data)) {
                String uid = data.getString("uid");
                //todo 查询企业信息
            }
        }
        return null;
    }

    public String cashNotice(JSONObject jsonObject) {
        log.info("提现回调 {}", jsonObject.toJSONString());
        if (StrUtil.equals(jsonObject.getString("status"), "true")) {
            JSONObject data = jsonObject.getJSONObject("data");
            if (Objects.nonNull(data)) {
                //todo 查询企业信息
            }
        }
        return null;
    }

    public String splitNotice(JSONObject jsonObject) {
        log.info("清分回调 {}", jsonObject.toJSONString());
        if (StrUtil.equals(jsonObject.getString("status"), "true")) {
            JSONObject data = jsonObject.getJSONObject("data");
            if (Objects.nonNull(data)) {
                //todo 查询企业信息
            }
        }
        return null;
    }


    /**
     * 提现
     * 重新提现：只针对汇付 3.20调整为只有新网渠道，其它渠道不接入
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Object cashOUt(CashWalletReq cashWalletReq) {
        log.info("提现：{}", JSONObject.toJSONString(cashWalletReq));
        Long entId = LoginUserHolder.getEntId();

        KwpWallet kwpWallet = kwpWalletMapper.selectOne(Wrappers.lambdaQuery(KwpWallet.class).eq(KwpWallet::getEntId, entId));
        if (Objects.isNull(kwpWallet)) {
            throw new BusinessException("企业钱包不存在");
        }
        Long amount = cashWalletReq.bigMoney(cashWalletReq.getMoney());
        long sub = kwpWallet.getCashAmount() - amount;
        if (sub < 0L) {
            throw new BusinessException("提现余额不足");
        }
        String key = String.format(RedisConstant.CASH_LOCK, entId);

        if (redisLockUtil.tryLock(key)) {
            try {
                KwpWalletCash kwpWalletCash = new KwpWalletCash();
                kwpWalletCash.setFromEnt(entId);
                kwpWalletCash.setToEnt(0L);
                kwpWalletCash.setOrderNo(IdUtil.getSnowflakeNextIdStr());
                kwpWalletCash.setAction(1);
                kwpWalletCash.setAmount(amount);
                kwpWalletCash.setCashAmount(sub);
                kwpWalletCash.setImg("");
                kwpWalletCash.setRemark(cashWalletReq.getRemark());
                kwpWalletCash.setStatus(3);
                kwpWalletCash.setCreateBy(0L);
                kwpWalletCash.setCreateTime(LocalDateTime.now());
                kwpWalletCash.setUpdateBy(0L);
                kwpWalletCash.setUpdateTime(LocalDateTime.now());
                kwpWalletCash.setDelFlag(0);


                WithdrawDto withdrawDto = new WithdrawDto();
                R<WithdrawRes> payIndexR = payXwService.withDraw(withdrawDto);
                if (!payIndexR.getStatus()) {
                    kwpWalletCash.setStatus(2);//提现失败
                } else {
                    //todo 提现成功在扣减，并更新主钱包
//                    kwpWallet.setCashAmount(sub);
//                    kwpWalletMapper.updateById(kwpWallet);
                }
                kwpWalletCashMapper.insert(kwpWalletCash);
                return kwpWalletCash.getId();
            } finally {
                redisLockUtil.unlock(key);
            }
        }
        throw new BusinessException("请稍后再试");

    }


    /**
     * 分转元
     *
     * @param big
     * @return
     */
    private BigDecimal smallMoney(Integer big) {
        if (Objects.isNull(big)) {
            return NumberConstant.ZERO_TWO;
        }
        return new BigDecimal(big).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
    }


    /**
     * 渠道开通
     *
     * @param createUserReq
     * @return
     */
    public Object createUser(CreateUserReq createUserReq) {
        Long entId = LoginUserHolder.getEntId();
        ChannelEnum channelEnum = ChannelEnum.getByChannel(createUserReq.getChannel());
        if (Objects.isNull(channelEnum)) {
            throw new BusinessException("支付通道不存在");
        }
        EntCacheResDto entCacheResDto = remoteSystemService.queryEntCacheById(entId);
        if (Objects.isNull(entCacheResDto)) {
            throw new BusinessException("未找到企业信息");
        }
        List<EntCertificateResDto> certificateInfo = entCacheResDto.getCertificateInfo();
        Map<Integer, String> certMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(certificateInfo)) {
            certMap = certificateInfo.stream().collect(Collectors.toMap(EntCertificateResDto::getType, EntCertificateResDto::getCode, (a, b) -> a));
        }
        //开通用户
        UserPara userPara = new UserPara();
        userPara.setUid("kll_" + IdUtil.getSnowflakeNextIdStr());
        userPara.setCompanyName(entCacheResDto.getFirmName());
        userPara.setLicense(certMap.get(3));
        userPara.setLegalIds(certMap.get(2));
        userPara.setLegalName(entCacheResDto.getLegalName());
        userPara.setLegalPhone(entCacheResDto.getLegalPhone());
        userPara.setNoticeUrl("");//todo 填写当前服务地址，通知开户成功
        return payXwService.beforeCreateUser(userPara);
    }


    /**
     * 贸易订单预支付下单时，冻结预付款金额
     *
     * @param payEntId     支付方企业
     * @param channel      渠道
     * @param receiveEntId 收款方企业
     * @param money        冻结金额（分）
     * @param orderNo      订单流水
     * @return
     */
    public HttpResult freezeMoney(Long payEntId, ChannelEnum channel, Long receiveEntId, Long money, String orderNo) {
        String uid = getUid(payEntId);
        String filter = getUid(receiveEntId);
        R<Freeze> freezeR = payCenterService.walletFreeze(uid, channel, filter, money, orderNo);
        if (!freezeR.getStatus()) {
            log.error("资金冻结异常：{}", JSONObject.toJSONString(freezeR));
            return HttpResult.error(freezeR.getMsg());
        }
        return HttpResult.ok("冻结成功");
    }


    /**
     * 钱包信息
     *
     * @return
     */
    public R<Object> userInfo(Long entId) {
        KwpWallet one = kwpWalletService.getOne(Wrappers.lambdaQuery(KwpWallet.class).eq(KwpWallet::getEntId, entId).last("limit 1"));
        if (Objects.nonNull(one)) {
            return R.ok(one);
        }
        return R.failed("暂未开通钱包");
    }


    public PageRes<CashVo> cashPage(com.sckw.payment.model.vo.req.page.CashPage cashPage) {
        log.info("可提现余额分页查询 {}", JSONObject.toJSONString(cashPage));
        PageHelper.startPage(cashPage.getPage(), cashPage.getPageSize());
        List<KwpWalletCash> kwpWalletCashes = kwpWalletCashMapper.selectList(new LambdaQueryWrapper<KwpWalletCash>()
                .eq(KwpWalletCash::getToEnt, LoginUserHolder.getEntId())
                .eq(Objects.nonNull(cashPage.getEntId()), KwpWalletCash::getFromEnt, cashPage.getEntId())
                .eq(Objects.nonNull(cashPage.getAction()), KwpWalletCash::getAction, cashPage.getAction())
                .eq(Objects.nonNull(cashPage.getStatus()), KwpWalletCash::getStatus, cashPage.getStatus())
                .between(Objects.nonNull(cashPage.getStartAmount()) && Objects.nonNull(cashPage.getEndAmount()), KwpWalletCash::getAmount, cashPage.getStartAmount(), cashPage.getEndAmount())
                .between(StrUtil.isNotBlank(cashPage.getStartCreateTime()) && StrUtil.isNotBlank(cashPage.getEndCreateTime()), KwpWalletCash::getCreateTime, cashPage.getStartCreateTime(), cashPage.getEndCreateTime())
                .like(StrUtil.isNotBlank(cashPage.getOrderNo()), KwpWalletCash::getOrderNo, cashPage.getOrderNo())
        );
        PageInfo<KwpWalletCash> pageInfo = new PageInfo<>(kwpWalletCashes);
        List<CashVo> collect = kwpWalletCashes.stream().map(d -> {
            CashVo bean = BeanUtil.toBean(d, CashVo.class);
            bean.setActionLabel(CashActionEnum.getByCode(d.getAction()));
            bean.setStatusLabel(CashStatusEnum.getByCode(d.getStatus()));
            bean.setCashAmount(MoneyUtil.smallMoney(d.getCashAmount()));
            bean.setAmount(MoneyUtil.smallMoney(d.getAmount()));
            return bean;
        }).collect(Collectors.toList());
        return PageRes.build(pageInfo, collect);
    }

    public PageRes<PrepayVo> prepayPage(PrePayPage prePayPage) { //todo
        PageHelper.startPage(prePayPage.getPage(), prePayPage.getPageSize());
        List<EntCacheResDto> entCacheResDtos = remoteSystemService.queryEntCacheByName(prePayPage.getEntName());
        Long entId = LoginUserHolder.getEntId();
        EntTypeResDto entTypeResDto = remoteSystemService.queryEntTypeById(entId);
        List<Integer> list = Arrays.asList(1, 2);
        Integer integer = Optional.ofNullable(entTypeResDto).map(EntTypeResDto::getType).orElse(0);
        if (!list.contains(integer)) {
            throw new BusinessException("企业类型异常");
        }
        LambdaQueryWrapper<KwpWalletPrepay> wrapper = Wrappers.lambdaQuery(KwpWalletPrepay.class);
        wrapper.eq(Objects.equals(integer, 1), KwpWalletPrepay::getToEnt, entId);//供应商
        wrapper.eq(Objects.equals(integer, 2), KwpWalletPrepay::getFromEnt, entId);//采购商

        wrapper.between(Objects.nonNull(prePayPage.getStartAmount()) && Objects.nonNull(prePayPage.getEndAmount()), KwpWalletPrepay::getPrepayAmount, prePayPage.getStartAmount(), prePayPage.getEndAmount());

        List<KwpWalletPrepay> kwpWalletPrepays = kwpWalletPrepayMapper.selectList(wrapper
                .eq(CollUtil.isNotEmpty(entCacheResDtos), KwpWalletPrepay::getId, entCacheResDtos.stream().map(EntCacheResDto::getId).toList())
        );
        PageInfo<KwpWalletPrepay> pageInfo = new PageInfo<>(kwpWalletPrepays);
        List<PrepayVo> collect = kwpWalletPrepays.stream().map(d -> {
            PrepayVo bean = BeanUtil.toBean(d, PrepayVo.class);
            bean.setActionLabel(PrepayActionEnum.getByCode(d.getAction()));
            bean.setStatusLabel(PrepayActionEnum.getByCode(d.getStatus()));
            return bean;
        }).collect(Collectors.toList());
        return PageRes.build(pageInfo, collect);
    }

    public PageRes<ListVo> prepayList(ListPage prePayPage) {
        PageHelper.startPage(prePayPage.getPage(), prePayPage.getPageSize());
        List<EntCacheResDto> entCacheResDtos = remoteSystemService.queryEntCacheByName(prePayPage.getEntName());
        Long entId = LoginUserHolder.getEntId();
        EntTypeResDto entTypeResDto = remoteSystemService.queryEntTypeById(entId);
        List<Integer> list = Arrays.asList(1, 2);
        Integer integer = Optional.ofNullable(entTypeResDto).map(EntTypeResDto::getType).orElse(0);
        if (!list.contains(integer)) {
            throw new BusinessException("企业类型异常");
        }
        LambdaQueryWrapper<KwpWalletSub> wrapper = Wrappers.lambdaQuery(KwpWalletSub.class);
        if (Objects.equals(integer, 1)) {// 供应商 收款方
            wrapper.eq(KwpWalletSub::getToEnt, entId);//
            wrapper.eq(CollUtil.isNotEmpty(entCacheResDtos), KwpWalletSub::getFromEnt,
                    entCacheResDtos.stream().map(EntCacheResDto::getId).toList());
        }
        if (Objects.equals(integer, 2)) {//采购商
            wrapper.eq(KwpWalletSub::getFromEnt, entId);
            wrapper.eq(CollUtil.isNotEmpty(entCacheResDtos), KwpWalletSub::getToEnt,
                    entCacheResDtos.stream().map(EntCacheResDto::getId).toList());
        }
        wrapper.between(Objects.nonNull(prePayPage.getStartAmount()) && Objects.nonNull(prePayPage.getEndAmount()),
                KwpWalletSub::getPrepayAmount, prePayPage.getStartAmount(), prePayPage.getEndAmount());


        List<KwpWalletSub> kwpWalletSubs = kwpWalletSubMapper.selectList(wrapper);
        PageInfo<KwpWalletSub> pageInfo = new PageInfo<>(kwpWalletSubs);

        List<ListVo> collect = kwpWalletSubs.stream().map(d -> {
            ListVo bean = BeanUtil.toBean(d, ListVo.class);
            if (Objects.equals(integer, 1)) {// 供应商 收款方
                bean.setFromEnt(d.getToEnt());
                bean.setFromEntName(getFirmName(d.getToEnt()));
                bean.setToEnt(d.getFromEnt());
                bean.setToEntName(getFirmName(d.getFromEnt()));
            }
            if (Objects.equals(integer, 2)) {
                bean.setFromEnt(d.getFromEnt());
                bean.setFromEntName(getFirmName(d.getFromEnt()));
                bean.setToEnt(d.getToEnt());
                bean.setToEntName(getFirmName(d.getToEnt()));
            }

            bean.setCashAmount(MoneyUtil.smallMoney(d.getCashAmount()));
            bean.setPrepayAmount(MoneyUtil.smallMoney(d.getPrepayAmount()));
            bean.setDealingAmount(MoneyUtil.smallMoney(d.getDealingAmount()));
            bean.setFreightAmount(MoneyUtil.smallMoney(d.getFreightAmount()));
            bean.setProcessingAmount(MoneyUtil.smallMoney(d.getProcessingAmount()));
            bean.setPayingAmount(MoneyUtil.smallMoney(d.getPayingAmount()));
            return bean;
        }).collect(Collectors.toList());
        return PageRes.build(pageInfo, collect);
    }


    public PageRes<ListVo> duePayList(ListPage listPage) {
        PageHelper.startPage(listPage.getPage(), listPage.getPageSize());
        List<EntCacheResDto> entCacheResDtos = remoteSystemService.queryEntCacheByName(listPage.getEntName());
        Long entId = LoginUserHolder.getEntId();
        EntTypeResDto entTypeResDto = remoteSystemService.queryEntTypeById(entId);
        List<Integer> list = Arrays.asList(1, 2);
        Integer integer = Optional.ofNullable(entTypeResDto).map(EntTypeResDto::getType).orElse(0);
        if (!list.contains(integer)) {
            throw new BusinessException("企业类型异常");
        }
        LambdaQueryWrapper<KwpWalletSub> wrapper = Wrappers.lambdaQuery(KwpWalletSub.class);
        if (Objects.equals(integer, 1) || Objects.equals(integer, 2)) {// 付款方
            wrapper.eq(KwpWalletSub::getFromEnt, entId);//
            wrapper.eq(CollUtil.isNotEmpty(entCacheResDtos), KwpWalletSub::getToEnt,
                    entCacheResDtos.stream().map(EntCacheResDto::getId).toList());
        }
        if (Objects.equals(integer, 3)) {//收款方
            wrapper.eq(KwpWalletSub::getToEnt, entId);
            wrapper.eq(CollUtil.isNotEmpty(entCacheResDtos), KwpWalletSub::getFromEnt,
                    entCacheResDtos.stream().map(EntCacheResDto::getId).toList());
        }
        wrapper.between(Objects.nonNull(listPage.getStartAmount()) && Objects.nonNull(listPage.getEndAmount()),
                KwpWalletSub::getPrepayAmount, listPage.getStartAmount(), listPage.getEndAmount());


        List<KwpWalletSub> kwpWalletSubs = kwpWalletSubMapper.selectList(wrapper);
        PageInfo<KwpWalletSub> pageInfo = new PageInfo<>(kwpWalletSubs);

        List<ListVo> collect = kwpWalletSubs.stream().map(d -> {
            ListVo bean = BeanUtil.toBean(d, ListVo.class);
            if (Objects.equals(integer, 1) || Objects.equals(integer, 2)) {//  付款方
                bean.setFromEnt(d.getFromEnt());
                bean.setFromEntName(getFirmName(d.getFromEnt()));
                bean.setToEnt(d.getToEnt());
                bean.setToEntName(getFirmName(d.getToEnt()));
            }
            if (Objects.equals(integer, 2)) {
                bean.setFromEnt(d.getToEnt());
                bean.setFromEntName(getFirmName(d.getToEnt()));
                bean.setToEnt(d.getFromEnt());
                bean.setToEntName(getFirmName(d.getFromEnt()));
            }

            bean.setCashAmount(MoneyUtil.smallMoney(d.getCashAmount()));
            bean.setPrepayAmount(MoneyUtil.smallMoney(d.getPrepayAmount()));
            bean.setDealingAmount(MoneyUtil.smallMoney(d.getDealingAmount()));
            bean.setFreightAmount(MoneyUtil.smallMoney(d.getFreightAmount()));
            bean.setProcessingAmount(MoneyUtil.smallMoney(d.getProcessingAmount()));
            bean.setPayingAmount(MoneyUtil.smallMoney(d.getPayingAmount()));
            return bean;
        }).collect(Collectors.toList());
        return PageRes.build(pageInfo, collect);
    }

    public Object prepayRecord(PayPage payPage) {
        PageHelper.startPage(payPage.getPage(), payPage.getPageSize());
        Long entId = LoginUserHolder.getEntId();


        LambdaQueryWrapper<KwpWalletPay> wrapper = Wrappers.lambdaQuery(KwpWalletPay.class);
        wrapper.eq(KwpWalletPay::getFromEnt, entId);
        wrapper.eq(KwpWalletPay::getToEnt, payPage.getToEnt());

        wrapper.eq(Objects.nonNull(payPage.getStatus()), KwpWalletPay::getStatus, payPage.getStatus());
        wrapper.between(StrUtil.isNotBlank(payPage.getStartCreateTime()) && StrUtil.isNotBlank(payPage.getEndCreateTime()),
                KwpWalletPay::getCreateTime, payPage.getStartCreateTime(), payPage.getEndCreateTime());


        List<KwpWalletPay> kwpWalletPays = kwpWalletPayMapper.selectList(wrapper);
        PageInfo<KwpWalletPay> pageInfo = new PageInfo<>(kwpWalletPays);


        List<PayVo> collect = kwpWalletPays.stream().map(d -> {
            PayVo bean = BeanUtil.toBean(d, PayVo.class);
            bean.setToEntName(getFirmName(d.getToEnt()));
            bean.setFromEntName(getFirmName(d.getFromEnt()));
            bean.setApplyAmount(MoneyUtil.smallMoney(d.getApplyAmount()));
            bean.setPayAmount(MoneyUtil.smallMoney(d.getPayAmount()));

            return bean;
        }).collect(Collectors.toList());
        return PageRes.build(pageInfo, collect);
    }
}
