package com.ywt.outpatient.controllers;

import com.google.protobuf.ByteString;
import com.ywt.biz.common.exception.YwtCommonException;
import com.ywt.biz.common.util.CacheUtil;
import com.ywt.biz.common.util.Checker;
import com.ywt.biz.common.util.StringHelper;
import com.ywt.gapi.ResultCode;
import com.ywt.gapi.base.redis.GetRedisKeysByPatternRequest;
import com.ywt.gapi.base.redis.GetRedisKeysByPatternResponse;
import com.ywt.gapi.base.redis.RedisHelperService;
import com.ywt.gapi.mq.ExecuteGRPCRequest;
import com.ywt.gapi.mq.ExecuteGRPCResponse;
import com.ywt.gapi.mq.MQExecutorService;
import com.ywt.gapi.operation_log.CreateLogRequest;
import com.ywt.gapi.operation_log.CreateLogResponse;
import com.ywt.gapi.operation_log.OperationLogService;
import com.ywt.gapi.system.GetCacheKeysRequest;
import com.ywt.gapi.system.GetCacheKeysResponse;
import com.ywt.gapi.system.InvalidateCacheRequest;
import com.ywt.gapi.system.SystemService;
import com.ywt.outpatient.api.req.InvalidateCacheReq;
import com.ywt.outpatient.api.req.OperationLogReq;
import com.ywt.outpatient.api.resp.BaseResponse2;
import com.ywt.outpatient.api.resp.CacheKeyResp;
import com.ywt.outpatient.api.resp.ListResponse;
import com.ywt.outpatient.core.BaseResponse;
import com.ywt.outpatient.core.utils.CheckUtil;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author Walker
 * Created on 2020/6/19
 */
@RestController("/dev")
@RequestMapping(value = {"/dev"})
public class DevController {
    private final Logger logger = LoggerFactory.getLogger(DevController.class);
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @DubboReference
    private MQExecutorService mqExecutorServiceBlockingStub;
    @DubboReference
    private SystemService systemServiceBlockingStub;
    @DubboReference
    private RedisHelperService redisHelperServiceBlockingStub;
    @DubboReference
    private OperationLogService operationLogServiceBlockingStub;

    @RequestMapping(value = {"/guavaCacheKeys"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<List<String>> keys(@RequestBody Void reqData) {
        BaseResponse2<List<String>> baseResponse = new BaseResponse2<>();
        try {
            List<String> list = new LinkedList<>(CacheUtil.getInstance().asMap().keySet());
            return baseResponse.succeed(list);
        } catch (Exception e) {
            logger.error("DevController#cacheKeys(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    @RequestMapping(value = {"/cacheKeys"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<ListResponse<CacheKeyResp>> cacheKeys(@RequestBody Void reqData) {
        BaseResponse2<ListResponse<CacheKeyResp>> baseResponse = new BaseResponse2<>();
        try {
            GetCacheKeysResponse response = systemServiceBlockingStub.getCacheKeys(GetCacheKeysRequest.newBuilder().build());
            if (response.getCode() == BaseResponse.SUCCEED) {
                ListResponse<CacheKeyResp> resp = new ListResponse<>();
                resp.setList(response.getKeysList().stream()
                        .map(key -> {
                            CacheKeyResp r = new CacheKeyResp();
                            r.setKey(key.getKeyName());
                            r.setCacheType(key.getCacheType());
                            r.setCallType(key.getCallType());
                            r.setKeyType(key.getKeyType());
                            r.setAlias(key.getAlias());
                            r.setRemark(key.getRemark());
                            r.setMethodName(key.getMethodName());
                            return r;
                        })
                        .collect(Collectors.toList()));
                return baseResponse.succeed(resp);
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, response.getMessage());
        } catch (Exception e) {
            logger.error("DevController#cacheKeys(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    @RequestMapping(value = {"/invalidateCache"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<Void> keys(@RequestBody InvalidateCacheReq reqData) {
        BaseResponse2<Void> baseResponse = new BaseResponse2<>();
        String key = reqData.getKey();
        if (StringHelper.isNullOrEmpty(key)) return baseResponse.succeed();
        int callType = Checker.getIntegerValue(reqData.getCallType());
        int cacheType = Checker.getIntegerValue(reqData.getCacheType());
        int keyType = Checker.getIntegerValue(reqData.getKeyType());
        try {
            switch (callType) {
                default:
                case InvalidateCacheReq.CALL_TYPE_LOCAL:
                    List<String> keys;
                    switch (cacheType) {
                        case InvalidateCacheReq.CACHE_TYPE_GUAVA:
                            keys = getKeys(key, keyType, cacheType);
                            logger.info("Keys: {}", keys);
                            for (String k : keys) {
                                CacheUtil.getInstance().invalidate(k);
                            }
                            return baseResponse.succeed("操作成功");
                        case InvalidateCacheReq.CACHE_TYPE_REDIS:
                            keys = getKeys(key, keyType, cacheType);
                            logger.info("Keys: {}", keys);
                            for (String k : keys) {
                                redisTemplate.delete(k);
                            }
                            return baseResponse.succeed();
                        default:
                            return baseResponse.failed(BaseResponse2.PARAMETER_ERROR, "未知缓存方式");
                    }
                case InvalidateCacheReq.CALL_TYPE_GRPC:
                    String methodName = Checker.getStringValue(reqData.getMethodName());
                    if (StringHelper.isNullOrEmpty(methodName))
                        return baseResponse.failedWithParameterError("请指定服务方法名称");
                    InvalidateCacheRequest request = InvalidateCacheRequest.newBuilder()
                            .setKey(key)
                            .setKeyType(keyType)
                            .setCacheType(cacheType)
                            .build();
                    ExecuteGRPCRequest executeGRPCRequest = ExecuteGRPCRequest.newBuilder()
                            .setMethod(methodName)
                            .setData(ByteString.copyFrom(request.toByteArray()))
                            .setPriority(1).build();
                    ExecuteGRPCResponse response = mqExecutorServiceBlockingStub.executeGRPC(executeGRPCRequest);
                    if (response.getCode() != ResultCode.SUCCEED_VALUE) {
                        return baseResponse.failedWithAppError(String.format("调用 MQ 服务发送消息失败，消息 id %s", response.getMsgId()));
                    }
                    return baseResponse.succeed();
                case InvalidateCacheReq.CALL_TYPE_REST:
                    // TODO: 2020/9/23 support rest if necessary
                    return baseResponse.succeed();
            }
        } catch (Exception e) {
            logger.error("DevController#invalidateCache(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    public List<String> getKeys(String keyValue, int keyType, int cacheType) {
        List<String> keys = new LinkedList<>();
        switch (keyType) {
            default:
            case InvalidateCacheReq.KEY_TYPE_MATCH:
                keys.add(keyValue);
                return keys;
            case InvalidateCacheReq.KEY_TYPE_PATTERN:
                switch (cacheType) {
                    // find all pattern-matched keys
                    case InvalidateCacheReq.CACHE_TYPE_GUAVA:
                        Pattern pattern = Pattern.compile(keyValue);
                        List<String> gks = new LinkedList<>(CacheUtil.getInstance().asMap().keySet());
                        return gks.stream().filter(pattern.asPredicate()).collect(Collectors.toList());
                    case InvalidateCacheReq.CACHE_TYPE_REDIS:
                        GetRedisKeysByPatternResponse response = redisHelperServiceBlockingStub.getRedisKeysByPattern(
                                GetRedisKeysByPatternRequest.newBuilder()
                                        .setPattern(keyValue)
                                        .build()
                        );
                        if (response.getCode() == ResultCode.SUCCEED_VALUE) {
                            return response.getKeysList();
                        } else {
                            logger.error("DevController#getKeys(keyValue={} , keyType={} , cacheType={} ):\n获取 keys 失败：{}",
                                    keyValue, keyType, cacheType, response.getMsg());
                        }
                    default:
                        return keys;
                }
        }
    }

    @RequestMapping(value = {"/opLog"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<String> opLog(@RequestBody OperationLogReq reqData) {
        BaseResponse2<String> baseResponse = new BaseResponse2<>();
        try {
            int terminal = Checker.getIntegerValue(reqData.getTerminal());
            int type = Checker.getIntegerValue(reqData.getType());
            int userId = Checker.getIntegerValue(reqData.getUserId());
            int userType = Checker.getIntegerValue(reqData.getUserType());
            String request = Checker.getStringValue(reqData.getRequest());
            String response = Checker.getStringValue(reqData.getResponse());
            String remark = Checker.getStringValue(reqData.getRemark());
            CheckUtil.ensureLargerThanZero(terminal, "终端号不正确");
            CheckUtil.ensureLargerThanZero(terminal, "操作类型不正确");
            CheckUtil.ensureLargerThanZero(userType, "用户类型不正确");
            CreateLogResponse resp = operationLogServiceBlockingStub.createLog(CreateLogRequest.newBuilder()
                    .setTerminal(terminal)
                    .setType(type)
                    .setUserId(userId)
                    .setUserType(userType)
                    .setRequest(request)
                    .setResponse(response)
                    .setRemark(remark)
                    .build());
            if (resp.getCode() == BaseResponse.SUCCEED) {
                return baseResponse.succeed(resp.getMsg(), resp.getMsg());
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, resp.getMsg());
        } catch (YwtCommonException appMessageException) {
            return baseResponse.error(appMessageException.getMessage());
        } catch (Exception e) {
            logger.error("DevController#opLog(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }
}
