package com.liquidnet.service.adam.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.common.mq.constant.MQConst;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.adam.constant.AdamMemberConst;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.AdamMemberOrderCallbackParam;
import com.liquidnet.service.adam.dto.AdamMemberOrderCodeParam;
import com.liquidnet.service.adam.dto.AdamMemberOrderParam;
import com.liquidnet.service.adam.dto.AdamMemberOrderResult;
import com.liquidnet.service.adam.dto.vo.*;
import com.liquidnet.service.adam.entity.AdamMemberOrder;
import com.liquidnet.service.adam.mapper.AdamMemberOrderMapper;
import com.liquidnet.service.adam.service.IAdamMemberOrderService;
import com.liquidnet.service.adam.service.IAdamMemberService;
import com.liquidnet.service.adam.service.IAdamRdmService;
import com.liquidnet.service.adam.service.IAdamUserMemberService;
import com.liquidnet.service.adam.service.admin.IAdamMemberPriceAdminService;
import com.liquidnet.service.adam.util.MemberUtil;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;

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

@Slf4j
@Service
public class AdamMemberOrderServiceImpl extends ServiceImpl<AdamMemberOrderMapper, AdamMemberOrder> implements IAdamMemberOrderService {
    @Autowired
    Environment env;
    @Autowired
    IAdamRdmService adamRdmService;
    @Autowired
    IAdamUserMemberService adamUserMemberService;
    @Autowired
    IAdamMemberPriceAdminService adamMemberPriceService;
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    MongoConverter mongoConverter;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    IAdamMemberService adamMemberService;

    @Override
    public List<AdamMemberOrderVo> getMemberOrderList(String uid) {
        String redisKey = AdamRedisConst.INFO_MEMBERS_ORDER_INFO.concat(uid);
        List<AdamMemberOrderVo> voList = new ArrayList<>();
        LinkedHashSet<String> hkeys = (LinkedHashSet<String>) redisUtil.hkeys(redisKey);
        if (hkeys.size() > 0) {
            Iterator<String> iterator = hkeys.iterator();
            while (iterator.hasNext()) {
                String key = iterator.next();
                AdamMemberOrderVo tempOrderVo = (AdamMemberOrderVo) redisUtil.hget(redisKey, key);
                if (null == tempOrderVo) {
                    tempOrderVo = mongoTemplate.findOne(Query.query(Criteria.where("orderNo").is(key)), AdamMemberOrderVo.class, AdamMemberOrderVo.class.getSimpleName());
                }
                if (null != tempOrderVo) {
                    voList.add(tempOrderVo);
                }
            }
        } else {
            voList = mongoTemplate.find(Query.query(Criteria.where("uid").is(uid)), AdamMemberOrderVo.class, AdamMemberOrderVo.class.getSimpleName());
            for (int i = 0; i < voList.size(); i++) {
                AdamMemberOrderVo vo = voList.get(i);
                redisUtil.hset(redisKey, vo.getOrderNo(), vo);
            }
        }
        return voList;
    }

    @Override
    public AdamMemberOrderVo getMemberOrderInfo(String uid, String orderNo) {
        AdamMemberOrderVo orderVo = (AdamMemberOrderVo) redisUtil.hget(AdamRedisConst.INFO_MEMBERS_ORDER_INFO.concat(uid), orderNo);
        if (null == orderVo) {
            orderVo = mongoTemplate.findOne(Query.query(Criteria.where("orderNo").is(orderNo).and("uid").is(uid)),
                    AdamMemberOrderVo.class, AdamMemberOrderVo.class.getSimpleName());

            if (null != orderVo) redisUtil.hset(AdamRedisConst.INFO_MEMBERS_ORDER_INFO.concat(uid), orderNo, orderVo);
        }
        return orderVo;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ResponseDto<AdamMemberOrderResult> buyMemberOrMemberCode(AdamMemberOrderParam param) {
        AdamMemberVo memberVo = adamRdmService.getMemberVoByMemberId(param.getMemberId());
        if (null == memberVo) {
            return ResponseDto.failure(ErrorMapping.get("10201"));
        }
        AdamMemberPriceVo memberPriceVo = adamRdmService.getMemberPriceVoByPriceId(param.getMemberId(), param.getMemberPriceId());
        if (null == memberPriceVo) {
            return ResponseDto.failure(ErrorMapping.get("10202"));
        }

        String createdAt = DateUtil.format(LocalDateTime.now(), DateUtil.Formatter.yyyyMMddHHmmss);
        // 生成订单信息
        AdamMemberOrderVo orderVo = new AdamMemberOrderVo();
        String orderNo = IDGenerator.nextSnowId().toString().concat("V");
        orderVo.setOrderNo(orderNo);
        orderVo.setMemberId(param.getMemberId());
        orderVo.setDays(memberPriceVo.getDays());
        orderVo.setMemberPriceId(param.getMemberPriceId());
        orderVo.setCreatedAt(createdAt);
        orderVo.setMemberName(memberVo.getName());
        orderVo.setMode(param.getMode());
        orderVo.setDeviceFrom(param.getDeviceFrom());
        orderVo.setPayType(param.getPayType());
        orderVo.setState(0);// 0-待支付
        orderVo.setUid(CurrentUtil.getCurrentUid());
        orderVo.setPrice(param.getPrice());
        orderVo.setIp(CurrentUtil.getCliIpAddr());
        orderVo.setVersion(CurrentUtil.getCliVersion());
        orderVo.setSource(CurrentUtil.getCliSource());

        if (!adamRdmService.setShotMemberOrderVoByOrderNo(orderNo, orderVo)) {
            log.warn("###购买会员创建订单失败[orderVo:{}]", JsonUtils.toJson(orderVo));
            return ResponseDto.failure(ErrorMapping.get("10210"));
        }

        // 调取对应支付接口
        LinkedMultiValueMap<String, String> payParam = new LinkedMultiValueMap<>();
        payParam.add("type", AdamMemberConst.TYPE_VIP);
        payParam.add("price", String.valueOf(memberPriceVo.getPrice()));
        payParam.add("name", orderVo.getMemberName());
        payParam.add("order_code", orderVo.getOrderNo());
        payParam.add("client_ip", orderVo.getIp());
        payParam.add("notify_url", env.getProperty("liquidnet.url-pay.callback"));
        payParam.add("create_date", createdAt);
        payParam.add("expire_time", AdamMemberConst.TIME_LIMIT.toString());
        payParam.add("detail", param.getMode() == 0 ? "正在现场 - 购买会员" : "正在现场 - 购买会员码");
        if (StringUtils.isNotBlank(param.getAuthCode())) {
            payParam.add("auth_code", param.getAuthCode());
        }
        if (StringUtils.isNotBlank(param.getOpenId())) {
            payParam.add("open_id", param.getOpenId());
        }
        if (StringUtils.isNotBlank(param.getProductId())) {
            payParam.add("product_id", param.getProductId());
        }

        AdamMemberOrderResult result = AdamMemberOrderResult.getNew();
        try {
            Map payResult = this.callPayServer(param.getDeviceFrom(), param.getPayType(), payParam);

            result.setCode((String) payResult.get("code"));
            result.setPayData(payResult.get("pay_data"));

            orderVo.setPayNo(result.getCode());
        } catch (Exception e) {
            log.warn("###购买会员下单调用支付中心异常[payParam:{}]", JsonUtils.toJson(payParam), e);

            return ResponseDto.failure(ErrorMapping.get("10203"));
        }
        adamRdmService.setShotMemberOrderVoByOrderNo(orderNo, orderVo);

        result.setOrderNo(orderVo.getOrderNo());
        result.setShowUrl(param.getShowUrl());
        result.setReturnUrl(param.getReturnUrl());
        return ResponseDto.success(result);
    }

    @Override
    public ResponseDto<AdamMemberOrderResult> exchangeMemberCode(AdamMemberOrderCodeParam param) {
        AdamMemberCodeVo memberCodeVo = adamRdmService.getMemberCodeVoByCode(param.getMemberCode());
        if (null == memberCodeVo) {
            return ResponseDto.failure(ErrorMapping.get("10206"));
        }
        if (memberCodeVo.getState() != 0) {
            return ResponseDto.failure(ErrorMapping.get(memberCodeVo.getState() == 1 ? "10207" : "10208"));
        }
        AdamMemberPriceVo memberPriceVo = adamRdmService.getMemberPriceVoByPriceId(memberCodeVo.getMemberId(), memberCodeVo.getMemberPriceId());
        if (null == memberPriceVo) {
            return ResponseDto.failure(ErrorMapping.get("10202"));
        }
        AdamMemberVo memberVo = adamRdmService.getMemberVoByMemberId(memberCodeVo.getMemberId());

        String currentDateTime = DateUtil.getNowTime();
        // 创建会员订单
        AdamMemberOrderVo initMemberOrderVo = AdamMemberOrderVo.getNew();
        initMemberOrderVo.setOrderNo(IDGenerator.nextSnowId().toString().concat("V"));
        initMemberOrderVo.setUid(CurrentUtil.getCurrentUid());
        initMemberOrderVo.setMemberCode(param.getMemberCode());
        initMemberOrderVo.setMemberNo(memberCodeVo.getMemberNo());
        initMemberOrderVo.setMemberId(IDGenerator.nextSnowId().toString());
        initMemberOrderVo.setMode(param.getMode());
        initMemberOrderVo.setMemberId(memberCodeVo.getMemberId());
        initMemberOrderVo.setMemberPriceId(memberCodeVo.getMemberPriceId());
        initMemberOrderVo.setMemberName(memberVo.getName());
        initMemberOrderVo.setState(1);// 0-待支付,1-已支付
        initMemberOrderVo.setCreatedAt(currentDateTime);
        initMemberOrderVo.setDays(memberPriceVo.getDays());
        initMemberOrderVo.setPayNo(memberCodeVo.getCode());
        initMemberOrderVo.setPrice(BigDecimal.valueOf(0));
        initMemberOrderVo.setPayType(initMemberOrderVo.getMode() == 2 ? "VIP_CODE" : "GIFT_CODE");
        initMemberOrderVo.setIp(CurrentUtil.getCliIpAddr());
        initMemberOrderVo.setSource(CurrentUtil.getCliSource());
        initMemberOrderVo.setVersion(CurrentUtil.getCliVersion());

        AdamUserMemberVo initUserMemberVo = AdamUserMemberVo.getNew();
        initUserMemberVo.setUid(initMemberOrderVo.getUid());
        initUserMemberVo.setMemberNo(initMemberOrderVo.getMemberNo());
        initUserMemberVo.setCreatedAt(currentDateTime);
        initUserMemberVo.setMemberId(initMemberOrderVo.getMemberId());
        initUserMemberVo.setState(1);
        initUserMemberVo.setUpdatedAt(currentDateTime);
        initUserMemberVo.setExpiryAt(
                DateUtil.format(
                        DateUtil.getBeforeDayEnd(Calendar.getInstance(), initMemberOrderVo.getDays()),
                        DateUtil.Formatter.yyyyMMddHHmmss
                )
        );

        LinkedList<String> toMqSqls = new LinkedList<>();
        LinkedList<Object[]> updateMemberCodeObjs = new LinkedList<>(),
                initMemberOrderObjs = new LinkedList<>(), initUserMemberObjs = new LinkedList<>();

        AdamMemberCodeVo updateMemberCodeVo = AdamMemberCodeVo.getNew();
        updateMemberCodeVo.setState(1);
        updateMemberCodeVo.setUpdatedAt(currentDateTime);
        updateMemberCodeVo.setUseOrderNo(initMemberOrderVo.getOrderNo());
        updateMemberCodeVo.setUseUid(initUserMemberVo.getUid());
        updateMemberCodeVo.setUseAt(currentDateTime);
        mongoTemplate.getCollection(AdamMemberCodeVo.class.getSimpleName()).updateOne(
                Query.query(Criteria.where("code").is(memberCodeVo.getCode())).getQueryObject(),
                new Document("$set", Document.parse(JsonUtils.toJson(updateMemberCodeVo)))
        );
        toMqSqls.add(SqlMapping.get("adam_member_code.exchange"));
        updateMemberCodeObjs.add(new Object[]{
                updateMemberCodeVo.getState(), updateMemberCodeVo.getUpdatedAt(), updateMemberCodeVo.getUseOrderNo(),
                updateMemberCodeVo.getUseUid(), updateMemberCodeVo.getUseAt(), memberCodeVo.getCode()
        });

        mongoTemplate.insert(initMemberOrderVo, AdamMemberOrderVo.class.getSimpleName());
        toMqSqls.add(SqlMapping.get("adam_member_order.add"));

        mongoTemplate.insert(initUserMemberVo, AdamUserMemberVo.class.getSimpleName());

        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.gets(toMqSqls, updateMemberCodeObjs, initMemberOrderObjs, initUserMemberObjs));

        adamRdmService.setUserMemberVoByUid(initMemberOrderVo.getUid(), initUserMemberVo);

        AdamMemberOrderResult result = AdamMemberOrderResult.getNew();
        result.setOrderNo(initMemberOrderVo.getOrderNo());
        return ResponseDto.success(result);
    }

    /**
     * 调用支付中心接口，获取支付信息
     *
     * @param payType  支付类型
     * @param payParam 支付参数
     * @return Map
     */
    private Map callPayServer(String deviceFrom, String payType, LinkedMultiValueMap<String, String> payParam) {
        String url = env.getProperty("liquidnet.url-pay.pay") + "/" + deviceFrom + "/" + payType;
        log.debug("HttpUtil.resquest.url:{},param:{}", url, JsonUtils.toJson(payParam));
        String json = HttpUtil.post(url, payParam);
        log.debug("HttpUtil.response.body:{}", json);
        return JsonUtils.fromJson(json, Map.class);
    }

    @Override
    public ResponseDto<Object> memberNotifyCallBack(AdamMemberOrderCallbackParam parameter) {
        AdamMemberOrderVo handleMemberOrderVo = mongoTemplate.findOne(
                Query.query(Criteria.where("orderNo").is(parameter.getOrderCode())),
                AdamMemberOrderVo.class, AdamMemberOrderVo.class.getSimpleName()
        );
        // AdamMemberConst.STATUS_*:0-待支付,1-已支付,2-已过期,3-超时付,4-退款中,5-退款完成
        if (1 == handleMemberOrderVo.getState() || 3 == handleMemberOrderVo.getState()) {
            return ResponseDto.failure(ErrorMapping.get("10502"), parameter);
        }
        if (parameter.getPrice().compareTo(handleMemberOrderVo.getPrice()) != 0) {
            // 订单金额比对
            return ResponseDto.failure(ErrorMapping.get("10503"), parameter);
        }
        AdamMemberPriceVo memberPriceVo =
                adamRdmService.getMemberPriceVoByPriceId(handleMemberOrderVo.getMemberId(), handleMemberOrderVo.getMemberPriceId());
        if (null == memberPriceVo) {
            return ResponseDto.failure(ErrorMapping.get("10500"));
        }
        /**
         * 购买方式[order.mode]:
         * 0-购买会员
         * 1-购买会员码(不需要填写地址)
         * 2-使用会员码(购买的) 仅限从未购买过会员的用户使用，需要填写地址
         * 3-使用礼包码(赠送的) 仅限从未购买过会员的用户使用，不需要填写地址
         */
        LinkedList<String> toMqSqls = new LinkedList<>();
        LinkedList<Object[]> operationObjs = new LinkedList<>(), updateMemberOrderObjs = new LinkedList<>();
        AdamMemberOrderVo updateMemberOrderVo = AdamMemberOrderVo.getNew();
        String currentDateTime = DateUtil.getNowTime();
        if (0 == handleMemberOrderVo.getMode()) {// 购买会员回调
            AdamUserMemberVo existUserMemberVo = adamRdmService.getUserMemberVoByUid(handleMemberOrderVo.getUid());
            if (null == existUserMemberVo) {// 创建会员
                String memberNo = adamUserMemberService.getNextMemberNo(handleMemberOrderVo.getMemberId());

                AdamUserMemberVo initUserMemberVo = AdamUserMemberVo.getNew();
                initUserMemberVo.setUid(handleMemberOrderVo.getUid());
                initUserMemberVo.setMemberId(handleMemberOrderVo.getMemberId());
                initUserMemberVo.setMemberNo(memberNo);
                initUserMemberVo.setState(1);// 1-正常,2-失效
                Date expiryAt = DateUtil.getBeforeDayEnd(Calendar.getInstance(), memberPriceVo.getDays());
                initUserMemberVo.setExpiryAt(DateUtil.format(expiryAt, DateUtil.Formatter.yyyyMMddHHmmss));
                initUserMemberVo.setCreatedAt(currentDateTime);

                // 订单写入MongoDB和Redis中
                mongoTemplate.insert(initUserMemberVo, AdamUserMemberVo.class.getSimpleName());
                adamRdmService.setUserMemberVoByUid(handleMemberOrderVo.getUid(), initUserMemberVo);

                updateMemberOrderVo.setMemberNo(memberNo);

                toMqSqls.add(SqlMapping.get("adam_user_member.add"));
                operationObjs.add(new Object[]{
                        initUserMemberVo.getUid(), initUserMemberVo.getMemberId(), initUserMemberVo.getMemberNo(),
                        initUserMemberVo.getState(), initUserMemberVo.getExpiryAt(), initUserMemberVo.getCreatedAt()
                });
            } else { // 续费
                String currentExpiryAt = existUserMemberVo.getExpiryAt();

                Date now = DateUtil.parse(currentDateTime, DateUtil.DATE_SMALL_STR),
                        currentExpiryAtDate = DateUtil.parse(currentExpiryAt, DateUtil.DATE_FULL_STR);
                currentExpiryAtDate = currentExpiryAtDate.before(now) ? now : currentExpiryAtDate;

                Calendar calendar = Calendar.getInstance();
                calendar.setTime(currentExpiryAtDate);
                Date expiryAt = DateUtil.getBeforeDayEnd(calendar, memberPriceVo.getDays());
                existUserMemberVo.setExpiryAt(DateUtil.format(expiryAt, DateUtil.Formatter.yyyyMMddHHmmss));
                existUserMemberVo.setState(1);// 1-正常,2-失效

                mongoTemplate.getCollection(AdamUserMemberVo.class.getSimpleName())
                        .updateOne(Query.query(Criteria.where("uid").is(existUserMemberVo.getUid())).getQueryObject(),
                                new Document("$set", Document.parse(JsonUtils.toJson(existUserMemberVo))));
                adamRdmService.setUserMemberVoByUid(handleMemberOrderVo.getUid(), existUserMemberVo);

                updateMemberOrderVo.setMemberNo(existUserMemberVo.getMemberNo());

                toMqSqls.add(SqlMapping.get("adam_user_member.update"));
                operationObjs.add(new Object[]{
                        existUserMemberVo.getState(), existUserMemberVo.getExpiryAt(), currentDateTime, existUserMemberVo.getUid()
                });
            }
        }
        if (1 == handleMemberOrderVo.getMode()) {// 购买会员码回调
            String memberNo = adamUserMemberService.getNextMemberNo(handleMemberOrderVo.getMemberId());

            AdamMemberCodeVo initMemberCodeVo = new AdamMemberCodeVo();
            initMemberCodeVo.setCode(MemberUtil.buyCode());
            initMemberCodeVo.setMemberId(handleMemberOrderVo.getMemberId());
            initMemberCodeVo.setMemberPriceId(handleMemberOrderVo.getMemberPriceId());
            initMemberCodeVo.setType(AdamMemberConst.TYPE_BUY);
            initMemberCodeVo.setMemberNo(memberNo);
            initMemberCodeVo.setState(0);
            initMemberCodeVo.setBuyOrderNo(parameter.getCode());
            initMemberCodeVo.setBuyUid(handleMemberOrderVo.getUid());
            initMemberCodeVo.setBuyAt(currentDateTime);
            initMemberCodeVo.setCreatedAt(currentDateTime);

            mongoTemplate.insert(initMemberCodeVo, AdamMemberCodeVo.class.getSimpleName());
            adamRdmService.setMemberCodeVoByCode(initMemberCodeVo.getCode(), initMemberCodeVo);

            updateMemberOrderVo.setMemberCode(initMemberCodeVo.getCode());
            updateMemberOrderVo.setMemberNo(memberNo);

            toMqSqls.add(SqlMapping.get("adam_member_code.add"));
            operationObjs.add(new Object[]{
                    initMemberCodeVo.getCode(), initMemberCodeVo.getType(), initMemberCodeVo.getMemberId(), initMemberCodeVo.getMemberPriceId(),
                    initMemberCodeVo.getMemberNo(), initMemberCodeVo.getState(), initMemberCodeVo.getCreatedAt(),
                    initMemberCodeVo.getBuyOrderNo(), initMemberCodeVo.getBuyUid(), initMemberCodeVo.getBuyAt()
            });
        }
        updateMemberOrderVo.setPaymentAt(parameter.getPaymentAt());
        updateMemberOrderVo.setState(AdamMemberConst.STATUS_PAID);

        mongoTemplate.getCollection(AdamMemberOrderVo.class.getSimpleName())
                .updateOne(Query.query(Criteria.where("orderNo").is(handleMemberOrderVo.getOrderNo())).getQueryObject(),
                        new Document("$set", Document.parse(JsonUtils.toJson(updateMemberOrderVo))));

        toMqSqls.add(SqlMapping.get("adam_member_order.update"));
        updateMemberOrderObjs.add(new Object[]{
                updateMemberOrderVo.getState(), updateMemberOrderVo.getMemberNo(), updateMemberOrderVo.getPaymentAt(),
                currentDateTime, handleMemberOrderVo.getOrderNo()
        });
        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.gets(toMqSqls, operationObjs, updateMemberOrderObjs));
        return ResponseDto.success();
    }
}
