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

import com.alibaba.fastjson.JSON;
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.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.IAdamMemberPriceService;
import com.liquidnet.service.adam.util.MemberUtil;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
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.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.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
    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 = (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
    public ResponseDto<AdamMemberOrderResult> buyMemberOrMemberCode(AdamMemberOrderParam param) {
        String memberId = param.getMemberId();
        String memberPriceId = param.getMemberPriceId();
        AdamMemberOrderResult result = new AdamMemberOrderResult();
        // 判断黑名单，待定（暂不做）

        // 验证会员是否有效
        AdamMemberVo adamMemberVo = adamMemberService.queryMemberInfo();
        if (!adamMemberVo.getMemberId().equals(memberId)) {
//            result.setState(0);
//            return result;
            return ResponseDto.failure(ErrorMapping.get("10201"));
        }
        // 验证价格是否有效
        List<AdamMemberPriceVo> adamMemberPrice = adamMemberVo.getPriceVoList();
        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;
            return ResponseDto.failure(ErrorMapping.get("10202"));
        }

        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.setMemberId(memberId);
        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(CurrentUtil.getCurrentUid());
        orderVo.setPrice(param.getPrice());
        orderVo.setIp(CurrentUtil.getCliIpAddr());
        orderVo.setVersion(CurrentUtil.getCliVersion());
        orderVo.setSource(CurrentUtil.getCliSource());

        mongoTemplate.insert(orderVo, AdamMemberOrderVo.class.getSimpleName());

        redisUtil.hset(AdamRedisConst.INFO_MEMBERS_ORDER_INFO.concat(orderVo.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", "127.0.0.1");
        payParam.add("notify_url", env.getProperty("liquidnet.url-pay.callback"));
        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 payResult = null;
//        switch (buyType) {
//            case 0:
//                payParam.add("detail", "正在现场 - 购买会员");
//                payResult = this.callPayServer(param.getPayChannel(), payParam);
//                result.setOrderNo((String) payResult.get("order_id"));
//                break;
//            case 1:
//                payParam.add("detail", "正在现场 - 购买会员码");
//                payResult = this.callPayServer(param.getPayChannel(), payParam);
//                result.setOrderNo((String) payResult.get("order_id"));
//                break;
//        }
        payParam.add("detail", buyType == 0 ? "正在现场 - 购买会员" : "正在现场 - 购买会员码");
        Map payResult = this.callPayServer(param.getPayChannel(), payParam);
        result.setOrderNo(orderVo.getOrderNo());
        result.setCode((String) payResult.get("code"));
        result.setPayData(payResult.get("pay_data"));
        result.setShowUrl(param.getShowUrl());
        result.setReturnUrl(param.getReturnUrl());
        return ResponseDto.success(result);
    }

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

        AdamUserMemberVo userMemberVo = (AdamUserMemberVo) redisUtil.hget(AdamRedisConst.INFO_MEMBERS_USER, 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("code").is(code)), AdamMemberCodeVo.class, AdamMemberCodeVo.class.getSimpleName());
        if (!memberCodeVo.getState().equals(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.setOrderNo(IDGenerator.nextSnowId().toString().concat("V"));
        orderVo.setMemberCode(code);
        orderVo.setUid(uid);
        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.setPayNo(memberCodeVo.getCode());
        orderVo.setPrice(BigDecimal.valueOf(0));

        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, AdamMemberOrderVo.class.getSimpleName());
        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, AdamUserMemberVo.class.getSimpleName());
        redisUtil.hset(AdamRedisConst.INFO_MEMBERS_USER, 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 统一通知队列插入数据库
        orderVo.setIp(CurrentUtil.getCliIpAddr());
        orderVo.setSource(CurrentUtil.getCliSource());
        orderVo.setVersion(CurrentUtil.getCliVersion());
        List<Object> mqList = AdamMemberOrderVo.getMemberOrderMqList(orderVo);
        /*
        ( `order_no`, `uid`, `mode`, `price`,
         `price_paid`, `member_name`, `member_id`,
         `member_price_id`, `days`, `state`, `member_no`,
         `pay_channel`, `pay_no`, `birthday`, `payment_at`,
         `created_at`, `updated_at`, `client_ip`, `source`, `version`)
         */
//        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                SqlMapping.get("adam_member_order.add", mqList.toArray()));
        return true;
    }

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

    @Override
    public boolean memberNotifyCallBack(AdamMemberOrderCallbackResult result) {
        if (1 != result.getStatus() ||
                !result.getType().equals(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.equals(orderVo.getMode())) { // 购买会员
                AdamUserMemberVo userMemberInfo = adamRdmService.getUserMemberVoByUid(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(), 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, AdamUserMemberVo.class.getSimpleName());
                    redisUtil.hset(AdamRedisConst.INFO_MEMBERS_USER, orderVo.getUid(), createMemberUser);
                    // 会员码使用记录
                    mongoTemplate.insert(codeVo, AdamMemberCodeVo.class.getSimpleName());
                    redisUtil.hset(AdamRedisConst.INFO_MEMBERS_CODE_INFO.concat(codeVo.getBuyUid()), codeVo.getBuyOrderNo(), codeVo);
                    // todo mq 发送数据到队列中

                } else { // 续费
                    String currentExpiryAt = userMemberInfo.getExpiryAt();

                    Date now = DateUtil.parse(DateUtil.getNowTime(), 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, 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.hset(AdamRedisConst.INFO_MEMBERS_USER, orderVo.getUid(), userMemberInfo);
                }
            }
            if (AdamMemberConst.SUB_TYPE_BUY_VIP_CODE.equals(orderVo.getMode())) { // 购买会员码
                cardNumber = adamUserMemberService.getMaxMemberNo(orderVo.getMemberId());
                codeVo = this.getAdamMemberCodeVo(orderVo, result.getCode(), currentDateTime);
                codeVo.setState(0);
                codeVo.setMemberNo(cardNumber);

                mongoTemplate.insert(codeVo, AdamMemberCodeVo.class.getSimpleName());
                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);

            List<Object> mqList = AdamMemberOrderVo.getMemberOrderMqList(orderVo);
        /*
        ( `order_no`, `uid`, `mode`, `price`,
         `price_paid`, `member_name`, `member_id`,
         `member_price_id`, `days`, `state`, `member_no`,
         `pay_channel`, `pay_no`, `birthday`, `payment_at`,
         `created_at`, `updated_at`, `client_ip`, `source`, `version`)
         */
//            rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                    SqlMapping.get("adam_member_order.add", mqList.toArray()));

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 组装会员码对象
     *
     * @param orderVo         会员订单
     * @param orderCode       订单号
     * @param currentDateTime 创建时间
     * @return AdamMemberCodeVo
     */
    private AdamMemberCodeVo getAdamMemberCodeVo(AdamMemberOrderVo orderVo, String orderCode, 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.buyCode());
        codeVo.setCreatedAt(currentDateTime);
        codeVo.setUpdatedAt(currentDateTime);
        return codeVo;
    }
}
