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.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.data.mongodb.core.query.Update;
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) {
        String memberId = param.getMemberId();
        String memberPriceId = param.getMemberPriceId();

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

        AdamMemberPriceVo memberPriceVo = adamRdmService.getMemberPriceVoByPriceId(memberId, memberPriceId);
        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(memberPriceVo.getDays());
        orderVo.setMemberPriceId(memberPriceId);
        orderVo.setCreatedAt(createdAt);
        orderVo.setMemberName(adamMemberVo.getName());
        orderVo.setMode(param.getMode());
        orderVo.setDeviceFrom(param.getDeviceFrom());
        orderVo.setPayType(param.getPayType());
        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());

        // 调取对应支付接口
        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());
        }
        payParam.add("detail", param.getMode() == 0 ? "正在现场 - 购买会员" : "正在现场 - 购买会员码");

        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.error("调用支付中心接口异常", e);

            return ResponseDto.failure(ErrorMapping.get("10203"));
        }

        mongoTemplate.insert(orderVo, AdamMemberOrderVo.class.getSimpleName());
        redisUtil.hset(AdamRedisConst.INFO_MEMBERS_ORDER_INFO.concat(orderVo.getUid()), orderNo, orderVo);

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

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

        AdamUserMemberVo userMemberVo = adamRdmService.getUserMemberVoByUid(uid);
        if (null != userMemberVo) {
            if (userMemberVo.getState() == 1 && LocalDateTime.now().isAfter(DateUtil.Formatter.yyyyMMddHHmmss.parse(userMemberVo.getExpiryAt()))) {
                return ResponseDto.failure(ErrorMapping.get("10205"));
            }
        }
        AdamMemberCodeVo memberCodeVo = mongoTemplate.findOne(Query.query(Criteria.where("code").is(code)), AdamMemberCodeVo.class, AdamMemberCodeVo.class.getSimpleName());
        if (memberCodeVo.getState() != 0) {
            return ResponseDto.failure(ErrorMapping.get(memberCodeVo.getState() == 1 ? "10206" : "10207"));
        }

        AdamMemberPriceVo memberPriceVo = adamRdmService.getMemberPriceVoByPriceId(memberCodeVo.getMemberId(), memberCodeVo.getMemberPriceId());
        if (null == memberPriceVo) {
            return ResponseDto.failure(ErrorMapping.get("10202"));
        }

        AdamMemberVo adamMemberVo = adamMemberService.queryMemberInfo();

        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.setMemberName(adamMemberVo.getName());
        orderVo.setState(AdamMemberConst.STATUS_UNPAID);
        orderVo.setCreatedAt(currentDateTime);
        orderVo.setDays(memberPriceVo.getDays());
        orderVo.setPayNo(memberCodeVo.getCode());
        orderVo.setPrice(BigDecimal.valueOf(0));

        if (AdamMemberConst.SUB_TYPE_USE_VIP_CODE == type) {// 会员码
            orderVo.setPayType(AdamMemberConst.VIP_CODE);
        } else if (AdamMemberConst.SUB_TYPE_USE_GIFT_CODE == type) {// 礼包码
            orderVo.setPayType(AdamMemberConst.GIFT_CODE);
        }

        // 创建用户使用兑换码的订单
        mongoTemplate.insert(orderVo, AdamMemberOrderVo.class.getSimpleName());
        redisUtil.hset(AdamRedisConst.INFO_MEMBERS_ORDER_INFO.concat(uid), orderVo.getOrderNo(), orderVo);

        if (null == userMemberVo) {
            userMemberVo = new AdamUserMemberVo();
            userMemberVo.setUid(uid);
            userMemberVo.setMemberNo(orderVo.getMemberNo());
            userMemberVo.setCreatedAt(currentDateTime);
        }
        userMemberVo.setMemberId(orderVo.getMemberId());
        userMemberVo.setState(1);
        userMemberVo.setUpdatedAt(currentDateTime);
        Date expiryAt = DateUtil.getBeforeDayEnd(Calendar.getInstance(), orderVo.getDays());
        String expiryAtStr = DateUtil.format(expiryAt, DateUtil.Formatter.yyyyMMddHHmmss);
        userMemberVo.setExpiryAt(expiryAtStr);

//        if (isNewMember) {
//            mongoTemplate.insert(userMemberVo, AdamUserMemberVo.class.getSimpleName());
//        } else {
//            mongoTemplate.getCollection(AdamUserMemberVo.class.getSimpleName()).updateOne(
//                    Query.query(Criteria.where("uid").is(uid)).getQueryObject(),
//                    new Document("$set", Document.parse(JsonUtils.toJson(userMemberVo)))
//            );
//        }
        Update update = Update.fromDocument(Document.parse(JsonUtils.toJson(userMemberVo)), "");
        mongoTemplate.upsert(Query.query(Criteria.where("uid").is(uid)), update, AdamUserMemberVo.class.getSimpleName());

        adamRdmService.setUserMemberVoByUid(uid, userMemberVo);

        // 更新旧数据
        memberCodeVo.setState(AdamMemberConst.STATUS_USED);
        mongoTemplate.getCollection(AdamMemberCodeVo.class.getSimpleName()).updateOne(
                Query.query(Criteria.where("code").is(memberCodeVo.getCode())).getQueryObject(),
                new Document("$set", Document.parse(JsonUtils.toJson(memberCodeVo)))
        );
        // 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()));

        AdamMemberOrderResult result = AdamMemberOrderResult.getNew();
        result.setOrderNo(orderVo.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();
    }
}
