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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.common.cache.redis.util.RedisUtil;
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.AdamMemberOrderCallbackResult;
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.entity.AdamMemberPrice;
import com.liquidnet.service.adam.mapper.AdamMemberOrderMapper;
import com.liquidnet.service.adam.mapper.AdamMemberPriceMapper;
import com.liquidnet.service.adam.service.IAdamMemberOrderService;
import com.liquidnet.service.adam.service.IAdamMemberService;
import com.liquidnet.service.adam.service.IAdamUserMemberService;
import com.liquidnet.service.adam.service.admin.IAdamMemberPriceService;
import com.liquidnet.service.adam.util.MemberUtil;
import com.mongodb.BasicDBObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
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.util.LinkedMultiValueMap;

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

@Slf4j
@Service
public class AdamMemberOrderServiceImpl extends ServiceImpl<AdamMemberOrderMapper, AdamMemberOrder> implements IAdamMemberOrderService {

    @Autowired
    AdamMemberOrderMapper adamMemberOrderMapper;
    @Autowired
    AdamMemberPriceMapper adamMemberPriceMapper;
    @Autowired
    IAdamUserMemberService adamUserMemberService;
    @Autowired
    IAdamMemberPriceService 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 = null;
        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)),
                    AdamMemberOrderVo.class, AdamMemberOrderVo.class.getSimpleName());
        }
        return orderVo;
    }

    @Override
    public AdamMemberOrderResult buyMemberOrMemberCode(AdamMemberOrderParam param) throws Exception {
        String memberId = param.getMemberId();
        String memberPriceId = param.getMemberPriceId();
        AdamMemberOrderResult result = new AdamMemberOrderResult();
        // 判断黑名单，待定（暂不做）

        // 验证会员是否有效
        AdamMemberVo adamMemberVo = adamMemberService.queryMemberInfo();
        if (adamMemberVo.getMemberId() != memberId) {
            result.setState(0);
            return result;
        }
        // 验证价格是否有效
        List<AdamMemberPriceVo> adamMemberPrice = adamMemberVo.getAdamMemberPrice();
        AdamMemberPriceVo priceVo = null;
        for (int i = 0; i < adamMemberPrice.size(); i++) {
            AdamMemberPriceVo adamMemberPriceVo = adamMemberPrice.get(i);
            if (adamMemberPriceVo.getMemberPriceId().equals(param.getMemberPriceId())) {
                priceVo = adamMemberPriceVo;
                break;
            }
        }
        if (null == priceVo) {
            result.setState(0);
            return result;
        }
        String cardNumber = "";
        // 查询用户是否是会员 获取会员编号
        AdamUserMemberVo userMemberInfo = adamUserMemberService.getUserMemberInfo(param.getUid());

        AdamMemberPriceVo memberPrice = (AdamMemberPriceVo) redisUtil.hget(AdamRedisConst.INFO_MEMBERS_PRICE_INFO.concat(memberId), memberPriceId);
        if (null == memberPrice) {
            memberPrice = (AdamMemberPriceVo) mongoTemplate.find(Query.query(Criteria.where("memberPriceId").is(memberPriceId)),
                    AdamMemberPriceVo.class, AdamMemberPriceVo.class.getSimpleName());
        }
        String createdAt = DateUtil.format(LocalDateTime.now(), DateUtil.Formatter.yyyyMMddHHmmss);
        // 生成订单信息
        AdamMemberOrderVo orderVo = new AdamMemberOrderVo();
        String orderNo = IDGenerator.nextSnowId().toString().concat("V");
        orderVo.setOrderNo(orderNo);
        orderVo.setMemberNo(cardNumber);
        orderVo.setMemberId(memberId);
        orderVo.setBirthday(DateUtil.format(param.getBirthday(), DateUtil.Formatter.yyyyMMddHHmmss));
        orderVo.setDays(memberPrice.getDays());
        orderVo.setMemberPriceId(memberPriceId);
        orderVo.setCreatedAt(createdAt);
        orderVo.setMemberName(adamMemberVo.getName());
        orderVo.setMode(param.getMode());
        orderVo.setPayChannel(param.getPayChannel());
        orderVo.setState(AdamMemberConst.STATUS_UNPAID);
        orderVo.setUid(param.getUid());
        orderVo.setPrice(param.getPrice());

        // todo 此处调用MQ通知mysql
//        List<Object> mqList = new ArrayList<>();

        mongoTemplate.insert(orderVo);

        redisUtil.hset(AdamRedisConst.INFO_MEMBERS_ORDER_INFO.concat(param.getUid()), orderNo, orderVo);

        // 创建订单
        int buyType = param.getMode();
        // 判断购买情况
        // 调取对应支付接口
        LinkedMultiValueMap<String, String> payParam = new LinkedMultiValueMap<>();
        payParam.add("type", AdamMemberConst.TYPE_VIP);
        payParam.add("price", String.valueOf(priceVo.getPrice()));
        payParam.add("name", orderVo.getMemberName());
        payParam.add("order_code", orderVo.getOrderNo());
        payParam.add("client_ip", param.getClientIp());
        payParam.add("notify_url", "回调地址");
        payParam.add("create_date", createdAt);
        payParam.add("expire_time", AdamMemberConst.TIME_LIMIT.toString());
        if (null != param.getAuthCode()) {
            payParam.add("auth_code", param.getAuthCode());
        }
        if (null != param.getOpenId()) {
            payParam.add("open_id", param.getOpenId());
        }
        if (null != param.getProductId()) {
            payParam.add("product_id", param.getProductId());
        }
        Map<String, String> payResult = null;
        switch (buyType) {
            case 0:
                payParam.add("detail", "正在现场 - 购买会员");
                payResult = this.callPayServer(param.getPayChannel(), payParam);
                result.setOrderId(payResult.get("order_id"));
                break;
            case 1:
                payParam.add("detail", "正在现场 - 购买会员码");
                payResult = this.callPayServer(param.getPayChannel(), payParam);
                result.setOrderId(payResult.get("order_id"));
                break;
        }
        result.setPayObject(payResult);
        return result;
    }

    /**
     * 使用会员码
     *
     * @param param
     * @return
     */
    @Override
    public boolean useMemberCode(AdamMemberOrderCodeParam param) {
        String uid = CurrentUtil.getCurrentUid();
        String code = param.getMemberCode();
        int type = param.getType();

        AdamUserMemberVo userMemberVo = (AdamUserMemberVo) redisUtil.get(AdamRedisConst.INFO_MEMBERS_USER_INFO.concat(uid));
        if (null == userMemberVo) {
            userMemberVo = mongoTemplate.findOne(Query.query(Criteria.where("uid").is(uid)), AdamUserMemberVo.class, AdamUserMemberVo.class.getSimpleName());
        }
        if (null != userMemberVo) {
            return false;
        }
        AdamMemberCodeVo memberCodeVo = mongoTemplate.findOne(Query.query(Criteria.where("memberCode").is(code)), AdamMemberCodeVo.class, AdamMemberCodeVo.class.getSimpleName());
        if (memberCodeVo.getState() != AdamMemberConst.STATUS_UNPAID) {
            return false;
        }

        // 获取会员价格对应的天数
        AdamMemberPriceVo priceVo = (AdamMemberPriceVo) redisUtil.hget(AdamRedisConst.INFO_MEMBERS_PRICE_INFO.concat(memberCodeVo.getMemberId()), memberCodeVo.getMemberPriceId());
        if (null == priceVo) {
            priceVo = mongoTemplate.findOne(Query.query(Criteria.where("memberPriceId").is(memberCodeVo.getMemberPriceId())), AdamMemberPriceVo.class, AdamMemberPriceVo.class.getSimpleName());
        }
        if (null == priceVo) {
            return false;
        }
        String currentDateTime = DateUtil.getNowTime();

        AdamMemberOrderVo orderVo = new AdamMemberOrderVo();
        orderVo.setMemberCode(code);
        orderVo.setMemberNo(memberCodeVo.getMemberNo());
        orderVo.setMemberId(IDGenerator.nextSnowId().toString());
        orderVo.setMode(type);
        orderVo.setMemberId(memberCodeVo.getMemberId());
        orderVo.setMemberPriceId(memberCodeVo.getMemberPriceId());
        orderVo.setState(AdamMemberConst.STATUS_UNPAID);
        orderVo.setCreatedAt(currentDateTime);
        orderVo.setDays(priceVo.getDays());
        orderVo.setBirthday(DateUtil.format(param.getBirthday(), DateUtil.Formatter.yyyyMMddHHmmss));

        if (AdamMemberConst.SUB_TYPE_USE_VIP_CODE == type) { //使用购买的会员码
            orderVo.setPayChannel(AdamMemberConst.VIP_CODE);
        } else if (AdamMemberConst.SUB_TYPE_USE_GIFT_CODE == type) { // 使用礼包码
            orderVo.setPayChannel(AdamMemberConst.GIFT_CODE);
        }

        // 创建用户使用兑换码的订单
        mongoTemplate.insert(orderVo);
        redisUtil.hset(AdamRedisConst.INFO_MEMBERS_ORDER_INFO.concat(uid), orderVo.getOrderNo(), orderVo);
        // 组装用户会员数据
        AdamUserMemberVo memberVo = new AdamUserMemberVo();
        memberVo.setMemberNo(orderVo.getMemberNo());
        memberVo.setMemberId(orderVo.getMemberId());
        memberVo.setState(AdamMemberConst.STATE_VALID);
        memberVo.setUid(uid);
        memberVo.setMemberUserId(IDGenerator.nextSnowId().toString());
        memberVo.setCreatedAt(currentDateTime);
        memberVo.setUpdatedAt(currentDateTime);

        Date expiryAt = DateUtil.getBeforeDayEnd(Calendar.getInstance(), orderVo.getDays());
        String expiryAtStr = DateUtil.format(expiryAt, DateUtil.Formatter.yyyyMMddHHmmss);
        memberVo.setExpiryAt(expiryAtStr);

        // 创建用户会员相关信息
        mongoTemplate.insert(memberVo);
        redisUtil.set(AdamRedisConst.INFO_MEMBERS_USER_INFO.concat(uid), memberVo);

        // 更新旧数据
        memberCodeVo.setState(AdamMemberConst.STATUS_USED);
        BasicDBObject object = new BasicDBObject("$set", JSON.parse(JsonUtils.toJson(memberCodeVo)));
        mongoTemplate.getCollection(AdamMemberCodeVo.class.getSimpleName())
                .updateOne(Query.query(Criteria.where("code").is(memberCodeVo.getCode())).getQueryObject(), object);
        // todo MQ 统一通知队列插入数据库

        return true;
    }

    /**
     * 调用支付中心接口，获取支付信息
     *
     * @param payType  支付类型
     * @param payParam 支付参数
     * @return
     */
    private Map<String, String> callPayServer(String payType, LinkedMultiValueMap<String, String> payParam) {
        String url = "http://testpay.zhengzai.tv";
        switch (payType) {
            case "APPALIPAY":
                url += "/app/alipay";
                break;
            case "APPWEPAY":
                url += "/app/wepay";
                break;
            case "WAPALIPAY":
                url += "/wap/alipay";
                break;
            case "WAPWEPAY":
                url += "/wap/wepay";
                break;
            case "JSWEPAY":
                url += "/js/wepay";
                break;
            case "APPLETWEPAY":
                url += "/applet/wepay";
                break;
        }
        String json = HttpUtil.post(url, payParam);
        HashMap<String, String> result = (HashMap<String, String>) JSONObject.parse(json);
        return result;
    }

    @Override
    public boolean memberNotifyCallBack(AdamMemberOrderCallbackResult result) {
        if (1 != result.getStatus() ||
                result.getType() != AdamMemberConst.TYPE_VIP ||
                null == result.getOrderCode() ||
                null == result.getCode()) {
            return false;
        }

        AdamMemberOrderVo orderVo = mongoTemplate.findOne(Query.query(Criteria.where("orderNo").is(result.getOrderCode())), AdamMemberOrderVo.class, AdamMemberOrderVo.class.getSimpleName());
        int state = orderVo.getState();
        if (AdamMemberConst.STATUS_PAID == state || AdamMemberConst.STATUS_LATE == state) {
            // 订单已经处理过
            return false;
        }
        if (result.getPrice().compareTo(orderVo.getPrice()) != 0) {
            // 金额不一致
            return false;
        }
        String currentDateTime = DateUtil.getNowTime();
        // 获取购买天数
        AdamMemberPriceVo priceVo = adamMemberPriceService.query(orderVo.getMemberId(), orderVo.getMemberPriceId());
        String cardNumber = "";
        AdamMemberCodeVo codeVo = null;
        int day = priceVo.getDays();
        try {
            if (AdamMemberConst.SUB_TYPE_BUY_VIP == orderVo.getMode()) { // 购买会员
                mongoTemplate.findOne(Query.query(Criteria.where("").is("")), null, null);
                AdamUserMemberVo userMemberInfo = adamUserMemberService.getUserMemberInfo(orderVo.getUid());
                if (null == userMemberInfo) { // 创建会员
                    cardNumber = adamUserMemberService.getMaxMemberNo(orderVo.getMemberId());
                    AdamUserMemberVo createMemberUser = AdamUserMemberVo.getNew();
                    createMemberUser.setMemberUserId(IDGenerator.nextSnowId().toString());
                    createMemberUser.setMemberId(orderVo.getMemberId());
                    createMemberUser.setMemberNo(orderVo.getMemberNo());
                    createMemberUser.setState(AdamMemberConst.STATE_VALID);
                    createMemberUser.setUid(orderVo.getUid());
                    createMemberUser.setCreatedAt(currentDateTime);
                    createMemberUser.setUpdatedAt(currentDateTime);
                    createMemberUser.setMemberNo(cardNumber);

                    Date expiryAt = DateUtil.getBeforeDayEnd(Calendar.getInstance(), day);
                    String expiryAtStr = DateUtil.format(expiryAt, DateUtil.Formatter.yyyyMMddHHmmss);
                    createMemberUser.setExpiryAt(expiryAtStr);

                    // todo  vip card code 记录  自动创建并使用
                    codeVo = this.getAdamMemberCodeVo(orderVo, result.getCode(), 0, currentDateTime);
                    codeVo.setState(1);
                    codeVo.setUseUid(orderVo.getUid());
                    codeVo.setUseOrderNo(orderVo.getMemberCode());
                    codeVo.setMemberNo(orderVo.getMemberNo());
                    codeVo.setUseAt(currentDateTime);
                    codeVo.setMemberCodeId(IDGenerator.nextSnowId().toString());

                    // 订单写入MongoDB和Redis中
                    mongoTemplate.insert(createMemberUser);
                    redisUtil.set(AdamRedisConst.INFO_MEMBERS_USER_INFO.concat(orderVo.getUid()), createMemberUser);
                    // 会员码使用记录
                    mongoTemplate.insert(codeVo);
                    redisUtil.hset(AdamRedisConst.INFO_MEMBERS_CODE_INFO.concat(codeVo.getBuyUid()), codeVo.getBuyOrderNo(), codeVo);
                    // todo mq 发送数据到队列中

                } else { // 续费
                    String currentExpiryAt = userMemberInfo.getExpiryAt();
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(DateUtil.parse(currentExpiryAt, "yyyy-MM-dd HH:mm:ss"));

                    Date expiryAt = DateUtil.getBeforeDayEnd(calendar, day);
                    String expiryAtStr = DateUtil.format(expiryAt, DateUtil.Formatter.yyyyMMddHHmmss);
                    userMemberInfo.setExpiryAt(expiryAtStr);

                    BasicDBObject object = new BasicDBObject("$set", JSON.parse(JsonUtils.toJson(userMemberInfo)));

                    mongoTemplate.getCollection(AdamUserMemberVo.class.getSimpleName())
                            .updateOne(Query.query(Criteria.where("uid").is(userMemberInfo.getUid())).getQueryObject(), object);

                    redisUtil.set(AdamRedisConst.INFO_MEMBERS_USER_INFO.concat(orderVo.getUid()), userMemberInfo);
                }
            }
            if (AdamMemberConst.SUB_TYPE_BUY_VIP_CODE == orderVo.getMode()) { // 购买会员码
                cardNumber = adamUserMemberService.getMaxMemberNo(orderVo.getMemberId());
                codeVo = this.getAdamMemberCodeVo(orderVo, result.getCode(), 0, currentDateTime);
                codeVo.setState(0);
                codeVo.setMemberNo(cardNumber);

                mongoTemplate.insert(codeVo);
                redisUtil.hset(AdamRedisConst.INFO_MEMBERS_CODE_INFO.concat(codeVo.getBuyUid()), codeVo.getBuyOrderNo(), codeVo);

            }
            // 更新订单信息
            orderVo.setPayChannel(result.getPaymentType());
            orderVo.setPaymentAt(DateUtil.format(result.getPaymentAt(), DateUtil.Formatter.yyyyMMddHHmmss));
            orderVo.setState(AdamMemberConst.STATUS_PAID);
            orderVo.setPayNo(result.getCode());
            orderVo.setMemberNo(cardNumber);
            if (null != codeVo) {
                orderVo.setMemberCode(codeVo.getCode()); // 兑换码数据
            }

            BasicDBObject object = new BasicDBObject("$set", JSON.parse(JsonUtils.toJson(orderVo)));

            mongoTemplate.getCollection(AdamMemberOrderVo.class.getSimpleName())
                    .updateOne(Query.query(Criteria.where("orderNo").is(orderVo.getOrderNo())).getQueryObject(), object);

            redisUtil.hset(AdamRedisConst.INFO_MEMBERS_ORDER_INFO.concat(orderVo.getUid()), orderVo.getOrderNo(), orderVo);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 组装会员码对象
     *
     * @param orderVo         会员订单
     * @param orderCode       订单号
     * @param type            会员码类型
     * @param currentDateTime 创建时间
     * @return
     */
    private AdamMemberCodeVo getAdamMemberCodeVo(AdamMemberOrderVo orderVo, String orderCode, int type, String currentDateTime) {
        AdamMemberCodeVo codeVo = new AdamMemberCodeVo();
        codeVo.setMemberId(orderVo.getMemberId());
        codeVo.setMemberPriceId(orderVo.getMemberPriceId());
        codeVo.setBuyAt(currentDateTime);
        codeVo.setBuyOrderNo(orderCode);
        codeVo.setType(AdamMemberConst.TYPE_BUY);
        codeVo.setMemberNo(orderVo.getMemberNo());
        codeVo.setCode(MemberUtil.getMemberCode(type));
        codeVo.setCreatedAt(currentDateTime);
        codeVo.setUpdatedAt(currentDateTime);
        return codeVo;
    }
}
