package com.czg.service.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import com.czg.account.dto.points.PointsExchangeRecordDTO;
import com.czg.account.entity.*;
import com.czg.account.enums.PointsExchangeStatusEnum;
import com.czg.account.enums.PointsOrderPayTypeEnum;
import com.czg.account.param.PointsExchangeCfParam;
import com.czg.account.param.PointsExchangeRecordParam;
import com.czg.account.param.PointsOrderCreateParam;
import com.czg.account.param.PointsOrderPayParam;
import com.czg.account.service.PointsExchangeRecordService;
import com.czg.account.vo.PointsExchangeSummaryVo;
import com.czg.exception.CzgException;
import com.czg.sa.StpKit;
import com.czg.service.account.mapper.*;
import com.czg.utils.HttpContextUtil;
import com.czg.utils.PageUtil;
import com.czg.validator.ValidatorUtil;
import com.czg.validator.group.DefaultGroup;
import com.czg.validator.group.InsertGroup;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * 积分兑换记录
 *
 * @author Tankaikai tankaikai@aliyun.com
 * @since 1.0 2025-02-25
 */
@AllArgsConstructor
@Service
public class PointsExchangeRecordServiceImpl extends ServiceImpl<PointsExchangeRecordMapper, PointsExchangeRecord> implements PointsExchangeRecordService {

    private final PointsGoodsSettingMapper pointsGoodsSettingMapper;
    private final MemberPointsMapper memberPointsMapper;
    private final MemberPointsLogMapper memberPointsLogMapper;
    private final ShopInfoMapper shopInfoMapper;
    private final ShopMerchantMapper shopMerchantMapper;
    private final PointsBasicSettingMapper pointsBasicSettingMapper;

    private QueryWrapper buildQueryWrapper(PointsExchangeRecordParam param) {
        QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
        if (StrUtil.isNotEmpty(param.getPickupMethod())) {
            queryWrapper.eq(PointsExchangeRecord::getPickupMethod, param.getPickupMethod());
        }
        if (StrUtil.isNotEmpty(param.getStatus())) {
            queryWrapper.eq(PointsExchangeRecord::getStatus, param.getStatus());
        }
        if (ObjUtil.isNotNull(param.getShopUserId())) {
            queryWrapper.eq(PointsExchangeRecord::getShopUserId, param.getShopUserId());
        }
        if (StrUtil.isNotEmpty(param.getKeywords())) {
            queryWrapper.and(q -> {
                q.like(PointsExchangeRecord::getOrderNo, param.getKeywords()).or(r -> {
                    r.like(PointsExchangeRecord::getCouponCode, param.getKeywords());
                });
            });
        }
        if (StrUtil.isNotEmpty(param.getBeginDate())) {
            queryWrapper.ge(PointsExchangeRecord::getCreateTime, param.getBeginDate() + " 00:00:00");
        }
        if (StrUtil.isNotEmpty(param.getEndDate())) {
            queryWrapper.le(PointsExchangeRecord::getCreateTime, param.getEndDate() + " 23:59:59");
        }
        Long shopId = StpKit.USER.getShopId(0L);
        queryWrapper.eq(PointsExchangeRecord::getShopId, shopId);
        queryWrapper.orderBy(PointsExchangeRecord::getId, false);
        return queryWrapper;
    }


    @Override
    public Page<PointsExchangeRecordDTO> getPointsExchangeRecordPage(PointsExchangeRecordParam param) {
        QueryWrapper queryWrapper = buildQueryWrapper(param);
        return super.pageAs(PageUtil.buildPage(), queryWrapper, PointsExchangeRecordDTO.class);
    }

    @Override
    public PointsExchangeRecordDTO getPointsExchangeRecordById(Long id) {
        Long shopId = StpKit.USER.getShopId(0L);
        return super.mapper.selectOneByQueryAs(query().eq(PointsExchangeRecord::getShopId, shopId).eq(PointsExchangeRecord::getId, id), PointsExchangeRecordDTO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkout(String couponCode) {
        if (StrUtil.isBlank(couponCode)) {
            throw new CzgException("兑换券券码不能为空");
        }
        PointsExchangeRecord entity = super.getOne(query().eq(PointsExchangeRecord::getCouponCode, couponCode));
        if (entity == null) {
            throw new CzgException("兑换券券码无效");
        }
        entity.setStatus(PointsExchangeStatusEnum.DONE.value());
        super.updateById(entity);
        PointsGoodsSetting goods = pointsGoodsSettingMapper.selectOneById(entity.getPointsGoodsId());
        if (goods == null) {
            throw new CzgException("积分商品不存在");
        }
        // 更新累计兑换数量
        goods.setTotalExchangeCount(goods.getTotalExchangeCount() + 1);
        pointsGoodsSettingMapper.update(goods);
    }

    private Object[] verify(PointsOrderCreateParam param) {
        ValidatorUtil.validateEntity(param, InsertGroup.class);
        PointsBasicSetting basic = pointsBasicSettingMapper.selectOneByQuery(query().eq(PointsBasicSetting::getShopId, param.getShopId()));
        if (basic == null) {
            throw new CzgException("商家未配置积分锁客基本设置");
        }
        if (basic.getEnablePointsMall() != 1) {
            throw new CzgException("积分商城未开启");
        }
        PointsGoodsSetting goods = pointsGoodsSettingMapper.selectOneById(param.getPointsGoodsId());
        if (goods == null) {
            throw new CzgException("兑换的商品信息不存在");
        }
        if (goods.getDelFlag() == 1) {
            throw new CzgException("兑换的商品信息不存在");
        }
        param.setPointsGoodsName(goods.getGoodsName());
        param.setGoodsImageUrl(goods.getGoodsImageUrl());

        Integer status = goods.getStatus();
        if (status != 1) {
            throw new CzgException("兑换的商品已下架");
        }
        Integer quantity = goods.getQuantity();
        if (quantity <= 0) {
            throw new CzgException("兑换的商品库存不足");
        }
        MemberPoints memberPoints = memberPointsMapper.selectOneByQuery(query().eq(MemberPoints::getShopUserId, param.getUserId()));
        if (memberPoints == null) {
            throw new CzgException("会员积分不足无法兑换此商品");
        }
        Integer accountPoints = memberPoints.getAccountPoints();
        Integer requiredPoints = goods.getRequiredPoints();
        if (accountPoints < requiredPoints) {
            throw new CzgException("会员积分不足无法兑换此商品");
        }
        return new Object[]{basic, goods, memberPoints};
    }

    @Override
    public PointsExchangeRecordDTO create(PointsOrderCreateParam param) {
        Long shopId = StpKit.USER.getShopId(0L);
        param.setShopId(shopId);
        Object[] verify = verify(param);
        PointsBasicSetting basic = (PointsBasicSetting) verify[0];
        PointsGoodsSetting goods = (PointsGoodsSetting) verify[1];
        MemberPoints memberPoints = (MemberPoints) verify[2];
        PointsExchangeRecord entity = BeanUtil.copyProperties(param, PointsExchangeRecord.class);
        entity.setExtraPaymentAmount(goods.getExtraPrice());
        entity.setSpendPoints(goods.getRequiredPoints());
        Snowflake seqNo = IdUtil.getSnowflake(0, 0);
        String orderNo = DateUtil.format(new Date(), "yyyyMMddHH") + StrUtil.subSuf(seqNo.nextIdStr(), -12);
        entity.setOrderNo(orderNo);
        entity.setStatus(PointsExchangeStatusEnum.UNPAID.value());
        // 生成订单
        super.save(entity);
        return BeanUtil.copyProperties(entity, PointsExchangeRecordDTO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PointsExchangeRecordDTO pay(PointsOrderPayParam param) {
        ValidatorUtil.validateEntity(param, DefaultGroup.class);
        PointsExchangeRecord entity = super.getById(param.getId());
        if (entity == null) {
            throw new CzgException("订单不存在");
        }
        try {
            PointsOrderCreateParam pointsOrderCreateParam = BeanUtil.copyProperties(entity, PointsOrderCreateParam.class);
            verify(pointsOrderCreateParam);
        } catch (Exception e) {
            throw new CzgException(e.getMessage().concat("，请您取消订单"));
        }
        if (!PointsExchangeStatusEnum.UNPAID.value().equals(entity.getStatus())) {
            throw new CzgException("订单状态异常，请刷新后重试");
        }
        entity.setPayType(param.getPayType().toUpperCase());
        if (!PointsOrderPayTypeEnum.getValues().contains(param.getPayType().toUpperCase())) {
            throw new CzgException("支付方式不合法");
        }
        // 设置支付方式
        entity.setPayMethod(PointsOrderPayTypeEnum.getText(param.getPayType().toUpperCase()));
        // 纯积分支付并且商品需要额外支付金额，需要抛出异常
        if (PointsOrderPayTypeEnum.POINTS.getValue().equals(entity.getPayType()) && NumberUtil.isGreater(entity.getExtraPaymentAmount(), BigDecimal.ZERO)) {
            throw new CzgException("此商品需要额外支付金额");
        }
        if (!PointsOrderPayTypeEnum.POINTS.getValue().equals(entity.getPayType()) && NumberUtil.equals(entity.getExtraPaymentAmount(), BigDecimal.ZERO)) {
            throw new CzgException("此商品不需要额外支付金额");
        }
        // 如果不需要额外支付
        if (NumberUtil.equals(entity.getExtraPaymentAmount(), BigDecimal.ZERO)) {
            // 设置核销券码
            entity.setCouponCode(IdUtil.getSnowflakeNextIdStr());
            entity.setStatus("waiting");
            entity.setPayTime(LocalDateTime.now());
            entity.setPayType(param.getPayType().toUpperCase());
            super.updateById(entity);
            waitingAfter(entity);
            return BeanUtil.copyProperties(entity, PointsExchangeRecordDTO.class);
        }
        if (StrUtil.isBlank(param.getOpenId())) {
            throw new CzgException("openId/userId不能为空");
        }
        HttpServletRequest request = HttpContextUtil.getHttpServletRequest();
        String ip = null;
        if (request != null) {
            ip = JakartaServletUtil.getClientIP(request);
        }
        if (StrUtil.isBlank(ip)) {
            throw new CzgException("无法获取用户ip地址");
        }
        // 需要额外支付
        ShopInfo shopInfo = shopInfoMapper.selectOneById(entity.getShopId());
        if (shopInfo == null) {
            throw new CzgException("店铺信息不存在");
        }
        ShopMerchant shopMerchant = shopMerchantMapper.selectOneById(shopInfo.getId());
        if (shopMerchant == null) {
            throw new CzgException("支付通道不存在");
        }
        if ("alipay".equalsIgnoreCase(entity.getPayType()) && StrUtil.isBlank(shopMerchant.getAlipaySmallAppid())) {
            throw new CzgException("店铺未配置支付宝小程序appId");
        }
        if (1 == 1) {
            // TODO 对接支付接口
            throw new CzgException("TODO暂未对接支付接口");
        }
        return null;
        /*// 准备支付参数
        // 应用ID
        String appId = thirdApply.getAppId();
        String appToken = thirdApply.getAppToken();
        String storeId = thirdApply.getStoreId();

        // 订单标题(商品名称)
        String subject = entity.getPointsGoodsName();
        // 商品描述
        String body = StrUtil.format("兑换商品：{} * {}", entity.getPointsGoodsName(), "1");
        // 交易金额 单位分 100 表示1元
        long amount = NumberUtil.mul(entity.getExtraPaymentAmount(), new BigDecimal("100")).longValue();
        // 支付方式
        String payType = entity.getPayType().toUpperCase();
        // 子商户APPID
        String subAppid = thirdApply.getSmallAppid();
        if ("alipay".equalsIgnoreCase(entity.getPayType())) {
            subAppid = thirdApply.getAlipaySmallAppid();
        }
        // 用户唯一标识 微信支付时，传入用户的openId；支付宝支付和银联支付时，传入用户的userId
        String openId = record.getOpenId();
        // 用户IP
        String clientIp = record.getIp();
        // 商户订单号	mchOrderNo
        String mchOrderNo = IdUtil.getSnowflakeNextIdStr();
        // 回调地址
        String notifyUrl = pointsGoodsOrderCallBack;
        // 支付成功后的跳转页面
        String returnUrl = null;
        PublicResp<WxScanPayResp> publicResp;
        try {
            publicResp = thirdPayService.scanpay(thirdUrl, appId, subject, body, amount, payType, subAppid, openId, clientIp, mchOrderNo, storeId, notifyUrl, returnUrl, appToken);
        } catch (Exception e) {
            log.error("拉起支付失败：", e);
            throw new MsgException(StrUtil.format("拉起支付失败：{}", e.getMessage()));
        }
        if (publicResp == null) {
            throw new MsgException("拉起支付失败：无响应");
        }
        if (!"000000".equals(publicResp.getCode())) {
            throw new MsgException(publicResp.getMsg());
        }
        WxScanPayResp payResp = publicResp.getObjData();
        if (!"TRADE_AWAIT".equals(payResp.getState())) {
            throw new MsgException(StrUtil.format("拉起支付失败：{}", payResp.getState()));
        }
        entity.setPayOrderId(payResp.getPayOrderId());
        super.updateById(entity);
        entity.setPayInfo(payResp.getPayInfo());
        return entity;*/
    }

    /**
     * 支付完成的后续操作
     *
     * @param entity 积分兑换实体
     */
    private void waitingAfter(PointsExchangeRecord entity) {
        PointsGoodsSetting goods = pointsGoodsSettingMapper.selectOneById(entity.getPointsGoodsId());
        if (goods == null) {
            throw new CzgException("积分商品不存在");
        }
        MemberPoints memberPoints = memberPointsMapper.selectOneById(entity.getShopUserId());
        if (memberPoints == null) {
            throw new CzgException("会员积分不足无法兑换此商品");
        }
        Integer quantity = goods.getQuantity();
        Integer accountPoints = memberPoints.getAccountPoints();
        Integer requiredPoints = goods.getRequiredPoints();
        // 扣减积分
        memberPoints.setAccountPoints(accountPoints - requiredPoints);
        memberPoints.setLastPointsChangeTime(LocalDateTime.now());
        memberPoints.setLastFloatPoints(-requiredPoints);
        memberPointsMapper.update(memberPoints);
        // 扣减库存
        goods.setQuantity(quantity - 1);
        pointsGoodsSettingMapper.update(goods);
        // 记录积分浮动流水
        MemberPointsLog log = new MemberPointsLog();
        log.setShopId(entity.getShopId());
        log.setShopUserId(entity.getShopUserId());
        log.setContent(StrUtil.format("兑换商品：{} * {}", entity.getPointsGoodsName(), "1"));
        log.setFloatType("subtract");
        log.setFloatPoints(-requiredPoints);
        memberPointsLogMapper.insert(log);
    }

    @Override
    public void cancel(PointsExchangeCfParam param) {
        if (param.getId() == null) {
            throw new CzgException("订单ID不能为空");
        }
        PointsExchangeRecord entity = super.getById(param.getId());
        if (entity == null) {
            throw new CzgException("订单不存在");
        }
        if (!PointsExchangeStatusEnum.UNPAID.value().equals(entity.getStatus())) {
            throw new CzgException("当前订单状态不支持取消");
        }
        entity.setStatus(PointsExchangeStatusEnum.CANCEL.value());
        entity.setCancelOrRefundReason(param.getCancelOrRefundReason());
        entity.setCancelOrRefundTime(LocalDateTime.now());
        super.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(PointsExchangeCfParam param) {
        if (param.getId() == null) {
            throw new CzgException("订单ID不能为空");
        }
        PointsExchangeRecord entity = super.getById(param.getId());
        if (entity == null) {
            throw new CzgException("订单不存在");
        }
        if (!PointsExchangeStatusEnum.WAITING.value().equals(entity.getStatus())) {
            throw new CzgException("当前订单状态不支持退款");
        }
        // 先退积分
        entity.setStatus(PointsExchangeStatusEnum.CANCEL.value());
        entity.setCancelOrRefundReason(param.getCancelOrRefundReason());
        entity.setCancelOrRefundTime(LocalDateTime.now());
        super.updateById(entity);
        MemberPoints memberPoints = memberPointsMapper.selectOneByQuery(query().eq(MemberPoints::getShopUserId, entity.getShopUserId()));
        if (memberPoints == null) {
            throw new CzgException("会员信息不存在");
        }
        memberPoints.setAccountPoints(memberPoints.getAccountPoints() + entity.getSpendPoints());
        memberPoints.setLastPointsChangeTime(LocalDateTime.now());
        memberPoints.setLastFloatPoints(entity.getSpendPoints());
        memberPointsMapper.update(memberPoints);
        // 回滚库存
        PointsGoodsSetting goods = pointsGoodsSettingMapper.selectOneById(entity.getPointsGoodsId());
        if (goods == null) {
            throw new CzgException("积分商品不存在");
        }
        goods.setQuantity(goods.getQuantity() + 1);
        pointsGoodsSettingMapper.update(goods);
        // 记录积分浮动流水
        MemberPointsLog log = new MemberPointsLog();
        log.setShopId(entity.getShopId());
        log.setShopUserId(entity.getShopUserId());
        log.setContent(StrUtil.format("(退单)兑换商品：{} * {}", entity.getPointsGoodsName(), "1"));
        log.setFloatType("add");
        log.setFloatPoints(+entity.getSpendPoints());
        log.setCreateTime(LocalDateTime.now());
        memberPointsLogMapper.insert(log);
        // 如果额外付款了则需要退款
        if (NumberUtil.equals(entity.getExtraPaymentAmount(), BigDecimal.ZERO)) {
            return;
        }
        // 需要额外退款
        ShopInfo shopInfo = shopInfoMapper.selectOneById(entity.getShopId());
        if (shopInfo == null) {
            throw new CzgException("店铺信息不存在");
        }
        ShopMerchant shopMerchant = shopMerchantMapper.selectOneById(shopInfo.getId());
        if (shopMerchant == null) {
            throw new CzgException("支付通道不存在");
        }
        if ("alipay".equalsIgnoreCase(entity.getPayType()) && StrUtil.isBlank(shopMerchant.getAlipaySmallAppid())) {
            throw new CzgException("店铺未配置支付宝小程序appId");
        }
        // 准备退款参数
        // 应用ID
        // TODO 对接退款接口
       /* 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 CzgException(StrUtil.format("发起退款失败：{}", e.getMessage()));
        }
        if (publicResp == null) {
            throw new CzgException("发起退款失败：无响应");
        }
        if (!"000000".equals(publicResp.getCode())) {
            throw new CzgException(publicResp.getMsg());
        }
        OrderReturnResp returnResp = publicResp.getObjData();
        if (!"SUCCESS".equals(returnResp.getState())) {
            throw new CzgException(StrUtil.format("退款失败原因：{}", returnResp.getState()));
        }*/
    }

    @Override
    public PointsExchangeSummaryVo total(PointsExchangeRecordParam param) {
        QueryWrapper queryWrapper = buildQueryWrapper(param);
        queryWrapper.select("count(*) as count,sum(extra_payment_amount) as totalAmount");
        PointsExchangeRecord summary = super.getOne(queryWrapper);
        PointsExchangeSummaryVo result = new PointsExchangeSummaryVo();
        result.setCount(summary.getCount());
        result.setTotalAmount(NumberUtil.nullToZero(summary.getTotalAmount()));
        return result;
    }

    @Override
    public void authCancel() {
        super.mapper.authCancel();
    }
}