package cn.ysk.cashier.mybatis.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapProxy;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.mybatis.entity.TbMemberPoints;
import cn.ysk.cashier.mybatis.entity.TbMemberPointsLog;
import cn.ysk.cashier.mybatis.entity.TbPointsExchangeRecord;
import cn.ysk.cashier.mybatis.entity.TbPointsGoodsSetting;
import cn.ysk.cashier.mybatis.mapper.*;
import cn.ysk.cashier.mybatis.service.TbPointsExchangeRecordService;
import cn.ysk.cashier.pojo.shop.TbMerchantThirdApply;
import cn.ysk.cashier.pojo.shop.TbShopInfo;
import cn.ysk.cashier.repository.shop.TbMerchantThirdApplyRepository;
import cn.ysk.cashier.thirdpay.resp.OrderReturnResp;
import cn.ysk.cashier.thirdpay.resp.PublicResp;
import cn.ysk.cashier.thirdpay.service.ThirdPayService;
import cn.ysk.cashier.utils.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 积分兑换记录
 *
 * @author Tankaikai tankaikai@aliyun.com
 * @since 2.0 2024-10-25
 */
@Service
public class TbPointsExchangeRecordServiceImpl extends ServiceImpl<TbPointsExchangeRecordMapper, TbPointsExchangeRecord> implements TbPointsExchangeRecordService {

    @Value("${thirdPay.url}")
    private String thirdUrl;

    @Resource
    private TbPointsGoodsSettingMapper tbPointsGoodsSettingMapper;

    @Resource
    private TbMemberPointsMapper tbMemberPointsMapper;

    @Resource
    private TbMemberPointsLogMapper tbMemberPointsLogMapper;

    @Resource
    private MpShopInfoMapper mpShopInfoMapper;
    @Resource
    private TbMerchantThirdApplyRepository tbMerchantThirdApplyRepository;

    @Resource
    private ThirdPayService thirdPayService;

    private LambdaQueryWrapper<TbPointsExchangeRecord> getWrapper(Map<String, Object> params) {
        MapProxy mapProxy = MapProxy.create(params);
        String keywords = mapProxy.getStr("keywords");
        String beginDate = mapProxy.getStr("beginDate");
        String endDate = mapProxy.getStr("endDate");
        TbPointsExchangeRecord param = BeanUtil.toBean(params, TbPointsExchangeRecord.class);

        LambdaQueryWrapper<TbPointsExchangeRecord> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(TbPointsExchangeRecord::getShopId, param.getShopId());
        wrapper.eq(StrUtil.isNotEmpty(param.getPickupMethod()), TbPointsExchangeRecord::getPickupMethod, param.getPickupMethod());
        wrapper.eq(StrUtil.isNotEmpty(param.getStatus()), TbPointsExchangeRecord::getStatus, param.getStatus());
        wrapper.eq(param.getMemberId() != null, TbPointsExchangeRecord::getMemberId, param.getMemberId());
        if (StrUtil.isNotEmpty(keywords)) {
            wrapper.nested(i -> i.like(TbPointsExchangeRecord::getOrderNo, keywords).or().like(TbPointsExchangeRecord::getCouponCode, keywords));
        }
        if (StrUtil.isNotEmpty(beginDate)) {
            wrapper.apply("create_time >= str_to_date({0}, '%Y-%m-%d %H:%i:%s')", beginDate + " 00:00:00");
        }
        if (StrUtil.isNotEmpty(endDate)) {
            wrapper.apply("create_time <= str_to_date({0}, '%Y-%m-%d %H:%i:%s')", endDate + " 23:59:59");
        }
        wrapper.orderByDesc(TbPointsExchangeRecord::getId);
        return wrapper;
    }

    @Override
    public Map<String, Object> page(Map<String, Object> params) {
        MapProxy mapProxy = MapProxy.create(params);
        int pageNum = mapProxy.getInt("page", 1);
        int pageSize = mapProxy.getInt("size", 10);
        LambdaQueryWrapper<TbPointsExchangeRecord> wrapper = getWrapper(params);
        Page<TbPointsExchangeRecord> page = super.page(new Page<>(pageNum, pageSize), wrapper);
        return PageUtil.toPlusPage(page.getRecords(), Convert.toInt(page.getTotal()));
    }

    @Override
    public TbPointsExchangeRecord get(Long id) {
        return super.getById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkout(String couponCode) {
        if (StrUtil.isBlank(couponCode)) {
            throw new BadRequestException("兑换券券码不能为空");
        }
        TbPointsExchangeRecord entity = super.getOne(Wrappers.<TbPointsExchangeRecord>lambdaQuery().eq(TbPointsExchangeRecord::getCouponCode, couponCode));
        if (entity == null) {
            throw new BadRequestException("兑换券券码无效");
        }
        entity.setStatus("done");
        entity.setUpdateTime(new Date());
        super.updateById(entity);
        TbPointsGoodsSetting goods = tbPointsGoodsSettingMapper.selectById(entity.getPointsGoodsId());
        if (goods == null) {
            throw new BadRequestException("积分商品不存在");
        }
        // 更新累计兑换数量
        goods.setTotalExchangeCount(goods.getTotalExchangeCount() + 1);
        goods.setUpdateTime(new Date());
        tbPointsGoodsSettingMapper.updateById(goods);
    }

    @Override
    public void cancel(TbPointsExchangeRecord record) {
        if (record.getId() == null) {
            throw new BadRequestException("订单ID不能为空");
        }
        TbPointsExchangeRecord entity = super.getById(record.getId());
        if (entity == null) {
            throw new BadRequestException("订单不存在");
        }
        if (!"unpaid".equals(entity.getStatus())) {
            throw new BadRequestException("当前订单状态不支持取消");
        }
        entity.setStatus("cancel");
        entity.setCancelOrRefundReason(record.getCancelOrRefundReason());
        entity.setCancelOrRefundTime(new Date());
        super.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(TbPointsExchangeRecord record) {
        if (record.getId() == null) {
            throw new BadRequestException("订单ID不能为空");
        }
        TbPointsExchangeRecord entity = super.getById(record.getId());
        if (entity == null) {
            throw new BadRequestException("订单不存在");
        }
        if (!"waiting".equals(entity.getStatus())) {
            throw new BadRequestException("当前订单状态不支持退款");
        }
        // 先退积分
        entity.setStatus("cancel");
        entity.setCancelOrRefundReason(record.getCancelOrRefundReason());
        entity.setCancelOrRefundTime(new Date());
        super.updateById(entity);
        TbMemberPoints memberPoints = tbMemberPointsMapper.selectOne(Wrappers.<TbMemberPoints>lambdaQuery().eq(TbMemberPoints::getMemberId, entity.getMemberId()));
        if (memberPoints == null) {
            throw new BadRequestException("会员信息不存在");
        }
        memberPoints.setAccountPoints(memberPoints.getAccountPoints() + entity.getSpendPoints());
        memberPoints.setLastPointsChangeTime(new Date());
        memberPoints.setLastFloatPoints(entity.getSpendPoints());
        tbMemberPointsMapper.updateById(memberPoints);
        // 回滚库存
        TbPointsGoodsSetting goods = tbPointsGoodsSettingMapper.selectById(entity.getPointsGoodsId());
        if (goods == null) {
            throw new BadRequestException("积分商品不存在");
        }
        goods.setQuantity(goods.getQuantity() + 1);
        goods.setUpdateTime(new Date());
        tbPointsGoodsSettingMapper.updateById(goods);
        // 记录积分浮动流水
        TbMemberPointsLog log = new TbMemberPointsLog();
        log.setShopId(entity.getShopId());
        log.setMemberId(entity.getMemberId());
        log.setMemberName(entity.getMemberName());
        log.setMobile(entity.getMobile());
        log.setAvatarUrl(entity.getAvatarUrl());
        log.setContent(StrUtil.format("(退单)兑换商品：{} * {}", entity.getPointsGoodsName(), "1"));
        log.setFloatType("add");
        log.setFloatPoints(+entity.getSpendPoints());
        log.setCreateTime(new Date());
        tbMemberPointsLogMapper.insert(log);
        // 如果额外付款了则需要退款
        if (NumberUtil.equals(entity.getExtraPaymentAmount(), BigDecimal.ZERO)) {
            return;
        }
        // 需要额外支付
        TbShopInfo shopInfo = mpShopInfoMapper.selectById(entity.getShopId());
        if (shopInfo == null) {
            throw new BadRequestException("店铺信息不存在");
        }
        TbMerchantThirdApply thirdApply = tbMerchantThirdApplyRepository.getById(Integer.valueOf(shopInfo.getMerchantId()));
        if (thirdApply == null) {
            throw new BadRequestException("支付通道不存在");
        }
        if ("alipay".equalsIgnoreCase(entity.getPayType()) && StrUtil.isBlank(thirdApply.getAlipaySmallAppid())) {
            throw new BadRequestException("店铺未配置支付宝小程序appId");
        }
        // 准备退款参数
        // 应用ID
        String appId = thirdApply.getAppId();
        String appToken = thirdApply.getAppToken();
        // 交易金额 单位分 100 表示1元
        long amount = NumberUtil.mul(record.getExtraPaymentAmount(), new BigDecimal("100")).longValue();
        // 退款订单号
        String mchRefundNo = entity.getOrderNo();
        PublicResp<OrderReturnResp> publicResp;
        try {
            publicResp = thirdPayService.returnOrder(thirdUrl, appId, mchRefundNo, entity.getPayOrderId(), null, entity.getCancelOrRefundReason(), amount, null, null, appToken);
        } catch (Exception e) {
            super.log.error("发起退款失败：", e);
            throw new BadRequestException(StrUtil.format("发起退款失败：{}", e.getMessage()));
        }
        if (publicResp == null) {
            throw new BadRequestException("发起退款失败：无响应");
        }
        if (!"000000".equals(publicResp.getCode())) {
            throw new BadRequestException(publicResp.getMsg());
        }
        OrderReturnResp returnResp = publicResp.getObjData();
        if (!"SUCCESS".equals(returnResp.getState())) {
            throw new BadRequestException(StrUtil.format("退款失败原因：{}", returnResp.getState()));
        }
    }

    @Override
    public Map<String, Object> total(Map<String, Object> params) {
        LambdaQueryWrapper<TbPointsExchangeRecord> wrapper = getWrapper(params);
        wrapper.select(TbPointsExchangeRecord::getCount, TbPointsExchangeRecord::getTotalAmount);
        TbPointsExchangeRecord summary = baseMapper.selectOne(wrapper);
        Map<String, Object> result = new HashMap<>(2);
        result.put("count", summary.getCount());
        result.put("totalAmount", NumberUtil.null2Zero(summary.getTotalAmount()));
        return result;
    }

    @Override
    public void authCancel() {
        baseMapper.update(Wrappers.<TbPointsExchangeRecord>lambdaUpdate()
                        .set(TbPointsExchangeRecord::getStatus, "cancel")
                        .set(TbPointsExchangeRecord::getCancelOrRefundTime, new Date())
                        .set(TbPointsExchangeRecord::getCancelOrRefundReason, "超时未支付，系统自动取消订单")
                .eq(TbPointsExchangeRecord::getStatus, "unpaid")
                .last("and TIMESTAMPDIFF(MINUTE, create_time, NOW()) >= 5"));
    }

}