package com.zm.order.tool.server.manager;

import com.alibaba.fastjson.JSON;
import com.aliyun.openservices.shade.com.google.common.collect.Maps;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.zm.order.tool.server.component.ThreadContext;
import com.zm.order.tool.server.enums.BizTypeEnum;
import com.zm.order.tool.server.exception.InnerApiException;
import com.zm.order.tool.server.inner.InnerApiRequest;
import com.zm.order.tool.server.inner.InnerApiSignature;
import com.zm.order.tool.server.model.Order;
import com.zm.order.tool.server.model.OrderPush;
import com.zm.order.tool.server.model.Refund;
import com.zm.order.tool.server.model.TraceIdentity;
import com.zm.order.tool.server.service.ExceptionConstants;
import com.zm.order.tool.server.utils.HttpUtils;
import com.zm.util.dto.Result;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @Description 内部接口管理
 * @Author: ph
 * @Date: 2020/1/6 14:23
 */
@Component
public class InnerApiManager {

    private static final Logger logger = LoggerFactory.getLogger(InnerApiManager.class);

    @Autowired
    @Qualifier("innerApiStorage")
    private JedisPool innerApiStorage;

    /**
     * 新餐饮
     * 超时自动取消通知
     */
    @Value("${catering.auto.cancel.order.routing.key}")
    private String cateringAutoCancelOrderRoutingKey;

    /**
     * 集中式消息队列app名称
     */
    private static final String MQ_API_APP_NAME = "coroutine";

    /**
     * 集中式消息队列app名称
     */
    private static final String API_APP_NAME = "coroutine";

    /**
     * 集中式消息队列路径
     */
    private static final String API_PATH = "mq/inner/send/msg";

    /**
     * 集中式消息队列路径
     */
    private static final String MQ_API_PATH = "mq/inner/send/msg";

    /**
     * 下载中心app名称
     */
    private static final String DOWNLOAD_API_APP_NAME = "download";
    /**
     * 下载中心app名称
     */
    private static final String ORDER_CENTER_NAME = "order-center";
    /**
     * 下载中心app名称
     */
    private static final String configCenterName = "config-center";

    private static final String GOODS_CENTER_NAME = "cygoods";

    /**
     * 添加下载计划
     */
    private static final String DOWNLOAD_API_ADD_PLAN = "plan/add";

    /**
     * 设置下载任务状态
     */
    private static final String DOWNLOAD_API_NOTICE_PLAN = "plan/update";

    /**
     * 订单详情
     */
    private static final String ORDER_DETAIL = "order/detail";

    /**
     * 根据商品标识等查询goodsId
     */
    private static final String TRADEMARK_GET_GOODSID = "goodsSku/trademark/get-goodsId";

    /**
     * 创建订单
     */
    private static final String ORDER_CREATE = "order/create";
    /**
     * 订单合并
     */
    private static final String ORDER_MERGE = "inner/order/account/merge";


    /**
     * 餐饮
     * 超时自动取消通知
     */
    @Value("${cy.auto.cancel.order.routing.key}")
    private String cyAutoCancelOrderRoutingKey;

    /**
     * 餐饮
     * 超时自动完成通知
     */
    @Value("${cy.auto.complete.order.routing.key}")
    private String cyAutoCompleteOrderRoutingKey;

    @Value("${cy.aggregate.order.create.routing.key}")
    private String cyCreateAggregateOrderRoutingKey;

    /**
     * 零售
     * 超时自动取消通知
     */
    @Value("${retail.auto.cancel.order.routing.key}")
    private String retailAutoCancelOrderRoutingKey;

    /**
     * 零售
     * 超时自动完成通知
     */
    @Value("${retail.auto.complete.order.routing.key}")
    private String retailAutoCompleteOrderRoutingKey;

    /**
     * 零售
     * 核销完成通知
     */
    @Value("${retail.notice.auto.verify.order.routing.key}")
    private String retailNoticeAutoVerifyOrderRoutingKey;

    /**
     * 订单推送队列
     */
    @Value("${order.push.notice.key}")
    private String orderPushNoticeKey;

    /**
     * 订单退款推送队列
     */
    @Value("${refund.push.notice.key}")
    private String refundPushNoticeKey;

    /**
     * 当前app名称
     */
    private static final String APP_NAME = "order-center";

    /**
     * 公共redis key
     */
    public static final String REDIS_KEY_PUBLIC_SERVICE = "public_services";

    /**
     * 数据格式
     */
    public static final String CONTENT_TYPE = "application/json";

    /**
     * 内部接口路由名
     */
    public static final String ROUTER_INNER_NAME = "qapi";

    /**
     * 路由连接串
     */
    public static final String ROUTER_SEPARATOR = "/";

    /**
     * 签名Key：from
     */
    public static final String SIGNATURE_KEY_FROM = "from";

    /**
     * 签名Key：timestamp
     */
    public static final String SIGNATURE_KEY_TIMESTAMP = "timestamp";

    /**
     * 签名Key：sign
     */
    public static final String SIGNATURE_KEY_SIGN = "sign";

    private LoadingCache<String, InnerApiSignature> loadingCache = CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.SECONDS)
            .build(new CacheLoader<String, InnerApiSignature>() {
                @Override
                public InnerApiSignature load(String appName) throws Exception {
                    return loadInnerApiSignature(appName);
                }
            });


    public Boolean configCenterSeller(Map<String, Object> body) {
        Result<Map> result = new Result();
        try {
            InnerApiSignature innerApiSignature = getInnerApiSignature(configCenterName);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(configCenterName);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature, ORDER_CREATE, body, orderInnerApiSignature.getAppKey());
            Result<Map> callResult = this.call(innerApiRequest);
//            logger.info("InnerApiManager[orderCreate]result!, callResult:{}, status:{}", callResult, callResult.isStatus());
            if (!callResult.isStatus()) {
                result.setException(callResult.exceptionInfo());
                return false;
            }
            return true;
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[orderDetail] failed, cause by:{}",
                    Throwables.getStackTraceAsString(throwable));
            return false;
        }
    }

    /**
     * 通知订单完成
     *
     * @return
     */
    public Result noticeOrderComplete(Order order) {
        Result result = new Result();
        try {
            Map<String, Object> param = Maps.newHashMap();
            BizTypeEnum bizTypeEnum = BizTypeEnum.getValue(order.getBizType());
            switch (bizTypeEnum) {
                case CY:
                    param.put("routing_key", cyAutoCompleteOrderRoutingKey);
                    break;
                case LS:
                    param.put("routing_key", retailAutoCompleteOrderRoutingKey);
                    break;
                case PT:
                    break;
                default:
            }
            Map<String, Object> innerParam = Maps.newHashMap();
            innerParam.put("orderNo", order.getOrderNo());
            innerParam.put("userId", order.getUserId());
            param.put("params", innerParam);
            InnerApiSignature innerApiSignature = getInnerApiSignature(API_APP_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature, API_PATH, param, orderInnerApiSignature.getAppKey());
            result = this.call(innerApiRequest);
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[noticeOrderComplete] failed, cause by:{}",
                    Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }

    public Result noticeAutoVerify(Order order){
        Result result = new Result();
        try {
            Map<String, Object> param = Maps.newHashMap();
            Map<String, Object> innerParam = Maps.newHashMap();
            param.put("routing_key", retailNoticeAutoVerifyOrderRoutingKey);
            innerParam.put("orderNo", order.getOrderNo());
            innerParam.put("userId", order.getUserId());
            param.put("params", innerParam);
            InnerApiSignature innerApiSignature = getInnerApiSignature(API_APP_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature, API_PATH, param, orderInnerApiSignature.getAppKey());
            result = this.call(innerApiRequest);
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[noticeOrderComplete] failed, cause by:{}",
                    Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }


    public Result noticeOrderCreate(Order order) {
        Result result = new Result();
        try {
            Map<String, Object> param = Maps.newHashMap();
            param.put("routing_key", cyCreateAggregateOrderRoutingKey);
            Map<String, Object> innerParam = Maps.newHashMap();
            innerParam.put("orderNo", order.getOrderNo());
            innerParam.put("userId", order.getUserId());
            innerParam.put("orderId", order.getId());
            param.put("params", innerParam);
            InnerApiSignature innerApiSignature = getInnerApiSignature(API_APP_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature, API_PATH, param, orderInnerApiSignature.getAppKey());
            result = this.call(innerApiRequest);
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[noticeOrderCreate] failed, cause by:{}",
                    Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }

    public Result noticePushOrder(OrderPush snapshot) {
        Result result = new Result();
        try {
            Map<String, Object> param = com.google.common.collect.Maps.newHashMap();
            param.put("routing_key", orderPushNoticeKey);
            Map<String, Object> innerParam = com.google.common.collect.Maps.newHashMap();
            innerParam.put("bizType", snapshot.getBizType());
            innerParam.put("orderNo", snapshot.getOrderNo());
            innerParam.put("userId", snapshot.getUserId());
            innerParam.put("sellerId", snapshot.getSellerId());
            innerParam.put("storeId", snapshot.getStoreId());
            innerParam.put("status", snapshot.getStatus());
            innerParam.put("version", snapshot.getVersion());
            innerParam.put("orderAt", snapshot.getOrderAt());
            innerParam.put("payStatus", snapshot.getPayStatus());
            innerParam.put("scene", snapshot.getScene());
            param.put("params", innerParam);
            InnerApiSignature innerApiSignature = getInnerApiSignature(MQ_API_APP_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature,
                    MQ_API_PATH,
                    param,
                    orderInnerApiSignature.getAppKey());
            result = this.call(innerApiRequest);
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[pushOrder] failed, cause by:{}", Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }


    /**
     * 通知订单取消
     *
     * @return
     */
    public Result noticeOrderCancel(Order order) {
        Result result = new Result();
        try {
            Map<String, Object> param = com.google.common.collect.Maps.newHashMap();
            BizTypeEnum bizTypeEnum = BizTypeEnum.getValue(order.getBizType());
            if (bizTypeEnum == null) {
                logger.error("[noticeOrderCancel]bizType is empty!orderNo:{}", order.getOrderNo());
                result.setException(ExceptionConstants.PARAM_BIZ_TYPE_IS_EMPTY);
                return result;
            }
            switch (bizTypeEnum) {
                case CY:
                    break;
                case LS:
                    break;
                case PT:
                    break;
                case BAKING:
                    break;
                case CATERING:
                    param.put("routing_key", cateringAutoCancelOrderRoutingKey);
                    break;
                default:
                    return Result.successNullData();
            }
            Map<String, Object> innerParam = com.google.common.collect.Maps.newHashMap();
            innerParam.put("bizType", order.getBizType());
            innerParam.put("orderNo", order.getOrderNo());
            innerParam.put("userId", order.getUserId());
            innerParam.put("sellerId", order.getSellerId());
            param.put("params", innerParam);
            InnerApiSignature mqInnerApiSignature = getInnerApiSignature(MQ_API_APP_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(mqInnerApiSignature, MQ_API_PATH, param, orderInnerApiSignature.getAppKey());
            result = this.call(innerApiRequest);
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[noticeOrderCancel] failed, cause by:{}",
                    Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }

    public Result noticePushRefund(Refund refund, String operation) {
        Result result = new Result();
        try {
            Map<String, Object> param = com.google.common.collect.Maps.newHashMap();
            param.put("routing_key", refundPushNoticeKey);
            Map<String, Object> innerParam = com.google.common.collect.Maps.newHashMap();
            innerParam.put("bizType", refund.getBizType());
            innerParam.put("orderNo", refund.getOrderNo());
            innerParam.put("userId", refund.getUserId());
            innerParam.put("refundNo", refund.getRefundNo());
            innerParam.put("sellerId", refund.getSellerId());
            innerParam.put("storeId", refund.getStoreId());
            innerParam.put("status", refund.getStatus());
            innerParam.put("version", refund.getVersion());
            innerParam.put("applyAt", refund.getApplyAt());
            innerParam.put("operation", operation);
            param.put("params", innerParam);
            InnerApiSignature innerApiSignature = getInnerApiSignature(MQ_API_APP_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature,
                    MQ_API_PATH,
                    param,
                    orderInnerApiSignature.getAppKey());
            result = this.call(innerApiRequest);
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[pushOrder] failed, cause by:{}", Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }

    public Result<Map> orderDetail() {
        Result<Map> result = new Result();
        try {
            Map<String, Object> body = com.google.common.collect.Maps.newHashMap();
            body.put("orderNo", "D76166200222515823014");
            body.put("userId", 772055115059429376L);
            InnerApiSignature innerApiSignature = getInnerApiSignature(ORDER_CENTER_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature, ORDER_DETAIL, body, orderInnerApiSignature.getAppKey());
            Result<Map> callResult = this.call(innerApiRequest);
            if (!callResult.isStatus()) {
                result.setException(callResult.exceptionInfo());
                return result;
            }
            result.setData(callResult.getData());
            result.setStatus(true);
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[orderDetail] failed, cause by:{}",
                    Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }

    public Boolean orderCreate(Map<String, Object> body) {
        Result<Map> result = new Result();
        try {
            InnerApiSignature innerApiSignature = getInnerApiSignature(ORDER_CENTER_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature, ORDER_CREATE, body, orderInnerApiSignature.getAppKey());
            Result<Map> callResult = this.call(innerApiRequest);
//            logger.info("InnerApiManager[orderCreate]result!, callResult:{}, status:{}", callResult, callResult.isStatus());
            if (!callResult.isStatus()) {
                result.setException(callResult.exceptionInfo());
                return false;
            }
            return true;
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[orderDetail] failed, cause by:{}",
                    Throwables.getStackTraceAsString(throwable));
            return false;
        }
    }

    public Boolean orderMerge(Map<String, Object> body) {
        Result<Map> result = new Result();
        try {
            InnerApiSignature innerApiSignature = getInnerApiSignature(ORDER_CENTER_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature, ORDER_MERGE, body, orderInnerApiSignature.getAppKey());
            Result<Map> callResult = this.call(innerApiRequest);
//            logger.info("InnerApiManager[orderCreate]result!, callResult:{}, status:{}", callResult, callResult.isStatus());
            if (!callResult.isStatus()) {
                result.setException(callResult.exceptionInfo());
                return false;
            }
            return true;
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[orderMerge] failed, cause by:{}",
                    Throwables.getStackTraceAsString(throwable));
            return false;
        }
    }

    private InnerApiSignature getInnerApiSignature(String appName) throws ExecutionException {
        return loadingCache.get(appName);
    }


    /**
     * 根据应用名称获取内部接口签名
     *
     * @param appName
     * @return
     */
    private InnerApiSignature loadInnerApiSignature(String appName) {
        Jedis jedis = null;
        try {
            jedis = innerApiStorage.getResource();
            String json = jedis.hget(REDIS_KEY_PUBLIC_SERVICE, appName);
            return JSON.parseObject(json, InnerApiSignature.class);
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[getInnerApiSignature] failed, request:{}, cause by:{}",
                    appName, Throwables.getStackTraceAsString(throwable));
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return null;
    }

    /**
     * 调用内部接口
     *
     * @param request
     * @return
     */
    private Result call(InnerApiRequest request) {
        CloseableHttpClient httpClient = HttpClients.custom().build();
        HttpUtils.Result httpResult = null;
        try {
            httpResult = HttpUtils.req(httpClient, request.getMethod(), request.getUrl(), request.getParams(), request.getHeaders(), request.getContentTypeOrDefault());
        } catch (HttpException e) {
            e.printStackTrace();
        } finally {
            HttpUtils.closeHttpClient(httpClient);
        }
        if (httpResult.isStatusOk()) {
            return JSON.parseObject(httpResult.getResponse(), Result.class);
        }
        throw new InnerApiException("调用内部方法错误");
    }

    private InnerApiRequest buildRequest(InnerApiSignature innerApiSignature, String path, Map<String, Object> param, String targetAppKey) {
        InnerApiRequest request = new InnerApiRequest();
        request.setAppName(APP_NAME);
        request.setBaseUri(innerApiSignature.getBaseUri());
        request.setUrl(joinUrl(innerApiSignature.getBaseUri(), path));
        request.setMethod(HttpUtils.MethodEnum.POST);
        request.setContentType(HttpUtils.ContentTypeEnum.JSON);
        request.setPath(path);
        Map<String, String> headers = Maps.newHashMap();
        TraceIdentity identity = ThreadContext.getTraceIdentity();
        if (identity != null) {
            String traceId = identity.getTraceId();
            if (!StringUtils.isEmpty(traceId)) {
                headers.put(TraceIdentity.HEADER_TRACE_ID, traceId);
            }
            headers.put(TraceIdentity.HEADER_TRACE_SPAN_ID, identity.getTraceSpanId());
            long traceStoreId = identity.getTraceStoreId();
            headers.put(TraceIdentity.HEADER_TRACE_STORE_ID, String.valueOf(traceStoreId));
            String from = identity.getFrom();
            if (!StringUtils.isEmpty(from)) {
                headers.put(TraceIdentity.HEADER_FROM, from);
            }
            String fromType = identity.getFromType();
            if (!StringUtils.isEmpty(fromType)) {
                headers.put(TraceIdentity.HEADER_FROM_TYPE, fromType);
            }
        }
        headers.put("Qmai-Api-From", APP_NAME);
        headers.put("Qmai-Version", request.getVersionOrDefault());
        headers.put("Referer", innerApiSignature.getBaseUri());
        headers.put(HTTP.CONTENT_TYPE, CONTENT_TYPE);
        headers.put("bizType", "5");
        request.setHeaders(headers);
        // 验签
        String nowTime = nowTime();
        param.put(SIGNATURE_KEY_TIMESTAMP, nowTime);
        param.put(SIGNATURE_KEY_SIGN, signature(targetAppKey, nowTime));
        request.setParams(param);
        return request;
    }

    /**
     * 签名
     *
     * @param appKey  签名密钥
     * @param nowTime 当前时间戳
     * @return 签名值
     */
    public String signature(String appKey, String nowTime) {
        String s = DigestUtils.md5Hex((DigestUtils.md5Hex(APP_NAME) + SIGNATURE_KEY_FROM + appKey));
        return DigestUtils.md5Hex(s + (DigestUtils.md5Hex(nowTime) + SIGNATURE_KEY_TIMESTAMP + appKey));
    }

    /**
     * 当前时间戳
     *
     * @return 时间戳，单位：秒
     */
    public String nowTime() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }

    /**
     * 拼接待访问接口的Url
     *
     * @param baseUri 待访问接口的域名，不用qapi结尾
     * @param path    待访问接口的路径，不用qapi开头
     */
    public String joinUrl(String baseUri, String path) throws InnerApiException {
        if (!baseUri.endsWith(ROUTER_SEPARATOR)) {
            baseUri += ROUTER_SEPARATOR;
        }
        if (!path.startsWith(ROUTER_SEPARATOR)) {
            path = ROUTER_SEPARATOR + path;
        }
        return baseUri + ROUTER_INNER_NAME + path;
    }


    public Result<Map> tradeMarksGetGoodsId(Map<String, Object> body) {
        Result<Map> result = new Result();
        try {
            InnerApiSignature innerApiSignature = getInnerApiSignature(GOODS_CENTER_NAME);
            InnerApiSignature orderInnerApiSignature = getInnerApiSignature(APP_NAME);
            InnerApiRequest innerApiRequest = buildRequest(innerApiSignature, TRADEMARK_GET_GOODSID, body, orderInnerApiSignature.getAppKey());
            logger.info("innerApiRequest[innerApiRequest],innerApiRequest:{}", innerApiRequest);

            Result<Map> callResult = this.call(innerApiRequest);
            if (!callResult.isStatus()) {
                result.setException(callResult.exceptionInfo());
                return result;
            }
            result.setData(callResult.getData());
            result.setStatus(true);
        } catch (Throwable throwable) {
            logger.error("InnerApiManager[tradeMarksGetGoodsId] failed, cause by:{}",
                    Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }

}
