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

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.liquidnet.common.mq.constant.MQConst;
import com.liquidnet.commons.lang.util.*;
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.domain.PageRequest;
import org.springframework.data.domain.Sort;
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
    IAdamMemberService adamMemberService;

    @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 memberOrderVo = new AdamMemberOrderVo();
        String orderNo = IDGenerator.nextSnowId().toString().concat("V");
        memberOrderVo.setOrderNo(orderNo);
        memberOrderVo.setMemberId(param.getMemberId());
        memberOrderVo.setDays(memberPriceVo.getDays());
        memberOrderVo.setMemberPriceId(param.getMemberPriceId());
        memberOrderVo.setCreatedAt(createdAt);
        memberOrderVo.setMemberName(memberVo.getName());
        memberOrderVo.setMode(param.getMode());
        memberOrderVo.setBirthday(param.getBirthday());
        memberOrderVo.setDeviceFrom(param.getDeviceFrom());
        memberOrderVo.setPayType(param.getPayType());
        memberOrderVo.setState(0);// 0-待支付
        memberOrderVo.setUid(CurrentUtil.getCurrentUid());
        memberOrderVo.setPrice(param.getPrice());
        memberOrderVo.setClientIp(CurrentUtil.getCliIpAddr());
        memberOrderVo.setVersion(CurrentUtil.getCliVersion());
        memberOrderVo.setSource(CurrentUtil.getCliSource());

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

        // 调取对应支付接口
        LinkedMultiValueMap<String, String> payParam = new LinkedMultiValueMap<>();
        payParam.add("type", "VIP");// VIP,CLUB
        payParam.add("price", String.valueOf(memberPriceVo.getPrice()));
        payParam.add("name", memberOrderVo.getMemberName());
        payParam.add("order_code", memberOrderVo.getOrderNo());
        payParam.add("client_ip", memberOrderVo.getClientIp());
        payParam.add("notify_url", env.getProperty("liquidnet.url-pay.callback"));
        payParam.add("create_date", createdAt);
        payParam.add("expire_time", "5");
        payParam.add("detail", "正在现场:购买会员".concat(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"));

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

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

        result.setOrderNo(memberOrderVo.getOrderNo());
        result.setShowUrl(param.getShowUrl());
        result.setReturnUrl(param.getReturnUrl());
        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> paymentNotifyCallBack(AdamMemberOrderCallbackParam parameter) {
        AdamMemberOrderVo handleMemberOrderVo = adamRdmService.getShotMemberOrderVoByOrderNo(parameter.getOrderCode());
        // 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);

                handleMemberOrderVo.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);

                handleMemberOrderVo.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(1);// 1-购买,2-赠送
            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);

            handleMemberOrderVo.setMemberCode(initMemberCodeVo.getCode());
            handleMemberOrderVo.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()
            });
        }
        handleMemberOrderVo.setPaymentAt(parameter.getPaymentAt());
        handleMemberOrderVo.setUpdatedAt(currentDateTime);
        handleMemberOrderVo.setState(1);// 1-已支付

        mongoTemplate.insert(handleMemberOrderVo, AdamMemberOrderVo.class.getSimpleName());
        toMqSqls.add(SqlMapping.get("adam_member_order.add"));
        updateMemberOrderObjs.add(new Object[]{
                handleMemberOrderVo.getOrderNo(), handleMemberOrderVo.getUid(), handleMemberOrderVo.getMode(), handleMemberOrderVo.getPrice(),
                handleMemberOrderVo.getPricePaid(), handleMemberOrderVo.getMemberName(), handleMemberOrderVo.getMemberId(), handleMemberOrderVo.getMemberPriceId(),
                handleMemberOrderVo.getDays(), handleMemberOrderVo.getState(), handleMemberOrderVo.getMemberNo(), handleMemberOrderVo.getBirthday(),
                handleMemberOrderVo.getDeviceFrom(), handleMemberOrderVo.getPayType(), handleMemberOrderVo.getPayNo(), handleMemberOrderVo.getPaymentAt(),
                handleMemberOrderVo.getCreatedAt(), handleMemberOrderVo.getClientIp(), handleMemberOrderVo.getSource(), handleMemberOrderVo.getVersion()
        });
        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.gets(toMqSqls, operationObjs, updateMemberOrderObjs));

        adamRdmService.setShotMemberOrderVoByOrderNo(handleMemberOrderVo.getOrderNo(), handleMemberOrderVo);
        return ResponseDto.success();
    }

    @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.setMode(param.getMode());
        initMemberOrderVo.setPrice(memberPriceVo.getPrice());
        initMemberOrderVo.setPricePaid(BigDecimal.ZERO);
        initMemberOrderVo.setMemberName(memberVo.getName());
        initMemberOrderVo.setMemberId(memberCodeVo.getMemberId());
        initMemberOrderVo.setMemberPriceId(memberCodeVo.getMemberPriceId());
        initMemberOrderVo.setDays(memberPriceVo.getDays());
        initMemberOrderVo.setState(1);// 0-待支付,1-已支付
        initMemberOrderVo.setMemberNo(memberCodeVo.getMemberNo());
        initMemberOrderVo.setBirthday(param.getBirthday());
        initMemberOrderVo.setDeviceFrom(param.getDeviceFrom());
        initMemberOrderVo.setPayType(param.getMode() == 2 ? "vipcode" : "giftcode");
        initMemberOrderVo.setPayNo(memberCodeVo.getCode());
        initMemberOrderVo.setPaymentAt(currentDateTime);
        initMemberOrderVo.setCreatedAt(currentDateTime);
        initMemberOrderVo.setClientIp(CurrentUtil.getCliIpAddr());
        initMemberOrderVo.setSource(CurrentUtil.getCliSource());
        initMemberOrderVo.setVersion(CurrentUtil.getCliVersion());

        initMemberOrderVo.setMemberCode(param.getMemberCode());

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

        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"));
        initMemberOrderObjs.add(new Object[]{
                initMemberOrderVo.getOrderNo(), initMemberOrderVo.getUid(), initMemberOrderVo.getMode(), initMemberOrderVo.getPrice(),
                initMemberOrderVo.getPricePaid(), initMemberOrderVo.getMemberName(), initMemberOrderVo.getMemberId(), initMemberOrderVo.getMemberPriceId(),
                initMemberOrderVo.getDays(), initMemberOrderVo.getState(), initMemberOrderVo.getMemberNo(), initMemberOrderVo.getBirthday(),
                initMemberOrderVo.getDeviceFrom(), initMemberOrderVo.getPayType(), initMemberOrderVo.getPayNo(), initMemberOrderVo.getPaymentAt(),
                initMemberOrderVo.getCreatedAt(), initMemberOrderVo.getClientIp(), initMemberOrderVo.getSource(), initMemberOrderVo.getVersion()
        });
        mongoTemplate.insert(initUserMemberVo, AdamUserMemberVo.class.getSimpleName());
        toMqSqls.add(SqlMapping.get("adam_user_member.add"));
        initMemberOrderObjs.add(new Object[]{
                initUserMemberVo.getUid(), initUserMemberVo.getMemberId(), initUserMemberVo.getMemberNo(),
                initUserMemberVo.getState(), initUserMemberVo.getExpiryAt(), initUserMemberVo.getCreatedAt()
        });

        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);
    }

    @Override
    public PageInfo<AdamMemberOrderSimpleVo> queryPage(String uid, int pageNo, int pageSize) {
        Query query = Query.query(Criteria.where("uid").is(uid));

        long count = mongoTemplate.count(query, AdamMemberOrderVo.class.getSimpleName());

        PageInfo<AdamMemberOrderSimpleVo> pageInfo = null;
        if (count > 0) {
            query.with(PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, "createdAt")));

            List<AdamMemberOrderSimpleVo> voList = mongoTemplate.find(
                    query, AdamMemberOrderSimpleVo.class, AdamMemberOrderVo.class.getSimpleName()
            );

            pageInfo = PageInfo.of(voList);
            pageInfo.setTotal(count);
        } else {
            pageInfo = new PageInfo<>();
        }
        return pageInfo;
    }
}
