package com.hzs.sale.ticket.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzs.common.core.constant.CacheConstants;
import com.hzs.common.core.constant.MemberFieldConstants;
import com.hzs.common.core.constant.msg.MemberMsgConstants;
import com.hzs.common.core.domain.R;
import com.hzs.common.core.enums.EDelFlag;
import com.hzs.common.core.enums.EOrderPrefix;
import com.hzs.common.core.enums.EOrderType;
import com.hzs.common.core.enums.ESpecialArea;
import com.hzs.common.core.exception.ServiceException;
import com.hzs.common.core.service.RedisService;
import com.hzs.common.core.utils.ComputeUtil;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.common.core.utils.reflect.ReflectUtils;
import com.hzs.common.domain.member.base.CuMember;
import com.hzs.common.domain.member.ext.CuMemberAccountExt;
import com.hzs.common.domain.sale.ext.SaOrderExt;
import com.hzs.common.domain.sale.order.SaOrder;
import com.hzs.common.domain.sale.ticket.SaBuyTicket;
import com.hzs.common.domain.sale.ticket.SaTicket;
import com.hzs.common.domain.sale.ticket.ext.SaTicketExt;
import com.hzs.common.util.TransactionUtils;
import com.hzs.member.base.IMemberServiceApi;
import com.hzs.sale.order.param.OrderParam;
import com.hzs.sale.order.service.ISaOrderService;
import com.hzs.sale.order.service.impl.SaOrderHandle;
import com.hzs.sale.ticket.mapper.SaTicketMapper;
import com.hzs.sale.ticket.param.BuyTicketParam;
import com.hzs.sale.ticket.param.SaTicketParam;
import com.hzs.sale.ticket.service.ISaBuyTicketService;
import com.hzs.sale.ticket.service.ISaTicketService;
import com.hzs.system.base.ICurrencyServiceApi;
import com.hzs.system.base.dto.CurrencyDTO;
import com.hzs.system.config.IAreaCurrencyServiceApi;
import com.hzs.system.config.dto.AreaCurrencyDTO;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 门票主表 服务实现类
 */
@Service
public class SaTicketServiceImpl extends ServiceImpl<SaTicketMapper, SaTicket> implements ISaTicketService {

    @DubboReference
    IAreaCurrencyServiceApi iAreaCurrencyServiceApi;
    @DubboReference
    ICurrencyServiceApi iCurrencyServiceApi;
    @DubboReference
    IMemberServiceApi iMemberServiceApi;

    @Autowired
    private SaOrderHandle saOrderHandle;
    @Autowired
    private ISaOrderService iSaOrderService;
    @Autowired
    private ISaBuyTicketService iSaBuyTicketService;
    @Autowired
    private RedisService redisService;

    @Override
    public void queryOrderSpendDetails(CuMemberAccountExt cuMemberAccount, BigDecimal payMoney, Long pkMember) {
        CuMember creator = iSaOrderService.getCuMemberByKey(pkMember);
        cuMemberAccount.setMemberCode(creator.getMemberCode());
        // 查询专区所属币种
        List<AreaCurrencyDTO> areaCurrencyDTOList = iAreaCurrencyServiceApi.queryAreaCurrencyByCondition(ESpecialArea.RETAIL_TICKET.getValue(), cuMemberAccount.getPkCountry()).getData();
        handleSaOrderAmount(cuMemberAccount, payMoney, areaCurrencyDTOList);
    }

    @Override
    public List<SaTicketExt> querySaTicketByParam(SaTicketParam saTicketParam) {
        return baseMapper.querySaTicketByParam(saTicketParam);
    }

    @Override
    public Boolean updateSaTicketByParam(Long pkMember, List<BuyTicketParam> ticketParamList) {
        iSaBuyTicketService.mergeSaBuyTicketService(pkMember, ticketParamList);
        return Boolean.TRUE;
    }

    @Override
    public Integer queryBuySaTicketNum(Long pkTicket) {
        return baseMapper.queryBuySaTicketNum(pkTicket);
    }

    @Override
    public SaTicket querySaTicketByParam(OrderParam orderParam, Long pkCreator) {
        LambdaQueryWrapper<SaTicket> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SaTicket::getOrderCode, orderParam.getOrderCode());
        queryWrapper.eq(SaTicket::getPkCreator, pkCreator);
        return baseMapper.selectOne(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelSaTicket(SaTicket saTicket, Long pkCreator) {
        // 撤销门票子表
        iSaBuyTicketService.deleteSaBuyTicketByPkId(saTicket.getPkId(), pkCreator);
        // 撤销门票主表
        LambdaUpdateWrapper<SaTicket> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(SaTicket::getPkId, saTicket.getPkId());
        updateWrapper.set(SaTicket::getDelFlag, EDelFlag.DELETE.getValue());
        updateWrapper.set(SaTicket::getPkModified, pkCreator);
        updateWrapper.set(SaTicket::getModifiedTime, DateUtils.currentDateTime());
        update(updateWrapper);
        // 撤销会员账号金额
        SaOrder saOrder = new SaOrder();
        saOrder.setPkId(saTicket.getPkId());
        saOrder.setPkCountry(saTicket.getPkCountry());
        saOrder.setPkCreator(saTicket.getPkCreator());
        saOrder.setOrderCode(saTicket.getOrderCode());
        saOrder.setPkMember(saTicket.getPkCreator());
        saOrder.setOrderType(EOrderType.RETAIL_TICKET.getValue());
        saOrder.setPkRate(saTicket.getPkRate());
        R<Boolean> flag = iMemberServiceApi.cancelMemberAccount(pkCreator, saOrder);
        if (!flag.isSuccess() || !flag.getData()) {
            throw new RuntimeException("会员回退账户金额失败！");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SaTicket paySaTicket(OrderParam orderParam, BigDecimal price, CuMemberAccountExt cuMemberAccountExt, String orderCode) {
        if (null == orderCode) {
            orderCode = saOrderHandle.createOrderCode(EOrderPrefix.ACTIVITY_CODE.getValue());
        }
        SaTicket saTicket = new SaTicket();
        saTicket.setOrderCode(orderCode);
        Date date = DateUtils.currentDateTime();
        // 获取汇率
        CurrencyDTO currency = iCurrencyServiceApi.getCurrency(orderParam.getPkSettleCountry()).getData();
        saTicket.setPkRate(currency.getPkId());
        saTicket.setPrice(price);
        saTicket.setQuantity(orderParam.getTicketParamList().size());
        saTicket.setOrderAmount(ComputeUtil.computeMultiply(saTicket.getPrice(), BigDecimal.valueOf(saTicket.getQuantity())));
        saTicket.setPkCreator(orderParam.getCenterCodeId());
        saTicket.setPkCountry(orderParam.getPkSettleCountry());
        saTicket.setPkTicket(orderParam.getPkTicket());
        saTicket.setCreationTime(date);
        save(saTicket);

        List<SaBuyTicket> saBuyTicketList = new ArrayList<>();
        for (BuyTicketParam buyTicketParam : orderParam.getTicketParamList()) {
            SaBuyTicket saBuyTicket = BeanUtil.copyProperties(buyTicketParam, SaBuyTicket.class);
            saBuyTicket.setPkSaTicket(saTicket.getPkId());
            saBuyTicket.setPkCreator(orderParam.getCenterCodeId());
            saBuyTicket.setPkCountry(orderParam.getPkSettleCountry());
            saBuyTicket.setCreationTime(date);
            saBuyTicketList.add(saBuyTicket);
        }
        CuMember cuMember = iMemberServiceApi.getMember(orderParam.getCenterCodeId()).getData();
        for (SaBuyTicket saBuyTicket : saBuyTicketList) {
            saBuyTicket.setPkTeam(cuMember.getPkVertex());
        }
        iSaBuyTicketService.saveBatch(saBuyTicketList);

        SaOrderExt saOrderExt = new SaOrderExt();
        saOrderExt.setPkCountry(orderParam.getPkSettleCountry());
        saOrderExt.setPkCreator(orderParam.getCenterCodeId());
        saOrderExt.setOrderAmount(saTicket.getOrderAmount());
        saOrderExt.setPostage(BigDecimal.ZERO);
        saOrderExt.setPkRate(saTicket.getPkRate());
        saOrderExt.setOrderCode(saTicket.getOrderCode());
        saOrderExt.setOrderType(EOrderType.RETAIL_TICKET.getValue());
        saOrderExt.setPayTime(date);
        saOrderExt.setPkId(saTicket.getPkId());
        R<Boolean> saveResult = iMemberServiceApi.saveMember(null, saOrderExt, cuMemberAccountExt);
        if (saveResult.isSuccess()) {
            if (!saveResult.getData()) {
                log.error("paySaTicket 保存订单失败!");
                throw new ServiceException("保存订单失败!");
            }
        } else {
            log.error("paySaTicket 保存订单失败!" + saveResult.getMsg());
            throw new ServiceException("保存订单失败!");
        }
        // 封装明细表
        return saTicket;
    }

    @Override
    public String querySaTicketOnlineOrderCode(OrderParam orderParam, BigDecimal price) {
        String orderCode = saOrderHandle.createOrderCode(EOrderPrefix.ACTIVITY_CODE.getValue());
        orderParam.setOrderCode(orderCode);
        orderParam.setPrice(price);
        redisService.setCacheObject(CacheConstants.TEMP_ORDER + orderParam.getCenterCodeId() + orderParam.getOrderCode(), orderParam, CacheConstants.LOGIN_TOKEN_REFRESH_TIME, TimeUnit.MINUTES);
        return orderCode;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySaTicketOnlineOrderCode(Long pkMember, String orderCode) {
        OrderParam orderParam = redisService.getCacheObject(CacheConstants.TEMP_ORDER + pkMember + orderCode);
        paySaTicket(orderParam, orderParam.getPrice(), null, orderCode);
    }

    private void handleSaOrderAmount(CuMemberAccountExt cuMemberAccount, BigDecimal payMoney, List<AreaCurrencyDTO> areaCurrencyDTOList) {
        if (!ComputeUtil.compareValue(payMoney)) {
            return;
        }
        cuMemberAccount.setIsPayment(Boolean.FALSE);
        // 原始支付金额（订单金额 + 邮费）
        BigDecimal original = payMoney;

        for (AreaCurrencyDTO areaCurrencyDTO : areaCurrencyDTOList) {
            // 开始扣款
            // 待支付金额>0进行扣减
            if (ComputeUtil.compareValue(payMoney)) {
                // 获得特殊处理
                // 判断比例是否 > 0, 为强制扣除金额 ，强制扣除金额不满足，直接支付失败
                if (!ComputeUtil.compareValue(areaCurrencyDTO.getDeductRatio())) {
                    // 支付比例为0处理
                    // 最大支付金额
                    if (null != areaCurrencyDTO.getDeductMaxRatio()
                            && areaCurrencyDTO.getDeductMaxRatio().compareTo(BigDecimal.ZERO) > 0) {
                        // 并且有最大支付比例也不为0，则需要进行使用限制
                        areaCurrencyDTO.setDeductMaxRatioAmount(ComputeUtil.computeDivide(original.multiply(areaCurrencyDTO.getDeductMaxRatio()), BigDecimal.valueOf(100)));
                    }
                    payMoney = saOrderHandle.subtract(payMoney, cuMemberAccount, areaCurrencyDTO);
                } else {
                    // 最小支付金额
                    BigDecimal minPayMoney = ComputeUtil.computeDivide(original.multiply(areaCurrencyDTO.getDeductRatio()), BigDecimal.valueOf(100));
                    BigDecimal accountValue = saOrderHandle.getAccountByKey(cuMemberAccount, areaCurrencyDTO.getFieldValue());
                    if (minPayMoney.compareTo(accountValue) > 0) {
                        if (cuMemberAccount.getErrorMsg() == null) {
                            cuMemberAccount.setErrorMsg(areaCurrencyDTO.getAccountName() + TransactionUtils.getContent(MemberMsgConstants.MEMBER_ACCOUNT_BALANCE_NOT_ENOUGH));
                        } else {
                            cuMemberAccount.setErrorMsg(cuMemberAccount.getErrorMsg() + areaCurrencyDTO.getAccountName() + TransactionUtils.getContent(MemberMsgConstants.MEMBER_ACCOUNT_BALANCE_NOT_ENOUGH));
                        }
                    }
                    BigDecimal originalPayMoney = ReflectUtils.invokeGetter(cuMemberAccount, MemberFieldConstants.PAY_ACCOUNT + areaCurrencyDTO.getFieldValue());
                    if (originalPayMoney == null) {
                        originalPayMoney = BigDecimal.ZERO;
                    }
                    ReflectUtils.invokeSetter(cuMemberAccount, MemberFieldConstants.PAY_ACCOUNT + areaCurrencyDTO.getFieldValue(), ComputeUtil.computeAdd(originalPayMoney, minPayMoney));
                    payMoney = payMoney.subtract(minPayMoney);
                }
                // 奖金币单独处理 消费的时候不需要处理，等实际扣款的时候需要处理，先扣除可提现，在扣除可消费
            }
            // 每次扣除后判断是否扣除完毕，扣除完毕结束
            if (payMoney.compareTo(BigDecimal.ZERO) == 0) {
                cuMemberAccount.setIsPayment(Boolean.TRUE);
                break;
            }
        }
    }

    @Override
    public Integer sumQuantity(Long pkTicket, Long pkMember) {
        return baseMapper.sumQuantity(pkTicket, pkMember);
    }

}
