package ma.luan.yiyan.service;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.eventbus.Message;
import io.vertx.core.eventbus.ReplyException;
import io.vertx.core.eventbus.ReplyFailure;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.redis.RedisClient;
import ma.luan.yiyan.constants.Key;
import ma.luan.yiyan.util.CategoryTrie;
import ma.luan.yiyan.util.JsonCollector;
import ma.luan.yiyan.util.OptionsUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;
import java.util.stream.Collectors;


public class DataService extends AbstractVerticle {
    private RedisClient redisClient;
    private Random random = new Random();
    private Logger log = LogManager.getLogger(this.getClass());
    private CategoryTrie keysInRedis = new CategoryTrie();

    @Override
    public void start(Future<Void> startFuture) {
        vertx.eventBus().consumer(Key.GET_GUSHICI_FROM_REDIS, this::getGushiciFromRedis);
        vertx.eventBus().consumer(Key.GET_HELP_FROM_REDIS, this::getHelpFromRedis);
        redisClient = RedisClient.create(vertx, OptionsUtil.getRedisOptions(config()));

        // 从 redis 缓存所有 key
        Future<JsonArray> imgKeys = Future.future(f -> redisClient.keys(Key.IMG, f));
        Future<JsonArray> jsonKeys = Future.future(f -> redisClient.keys(Key.JSON, f));
        CompositeFuture.all(Arrays.asList(imgKeys, jsonKeys)).setHandler(v -> {
            if (v.succeeded()) {
                imgKeys.result().addAll(jsonKeys.result())
                    .stream()
                    .forEach(key -> keysInRedis.insert((String) key));
                startFuture.complete();
            } else {
                log.error("DataService fail to start", v.cause());
                startFuture.fail(v.cause());
            }
        });
    }

    private void getHelpFromRedis(Message message) {
        redisClient.lrange(Key.REDIS_HELP_LIST, 0, -1, res -> {
            if (res.succeeded()) {
                JsonArray array = res.result();
                JsonArray newArray = array.stream()
                    .map(text -> {
                        String prefix = config().getString("api.url", "http://localhost/");
                        return new JsonObject((String) text).stream()
                            .collect(Collectors.toMap(Map.Entry::getKey,
                                v -> prefix + v.getValue().toString().replace(":", "/")));
                    })
                    .collect(JsonCollector.toJsonArray());
                message.reply(newArray);
            } else {
                log.error("Fail to get data from Redis", res.cause());
                message.fail(500, res.cause().getMessage());
            }
        });
    }

    /**
     * @param message example: {format: "png", categories: [shenghuo, buyi]}
     */
    private void getGushiciFromRedis(Message<JsonObject> message) {
        JsonArray realCategory = new JsonArray()
            .add("png".equals(message.body().getString("format")) ? "img" : "json")
            .addAll(message.body().getJsonArray("categories"));
        checkAndGetKey(realCategory)
            .compose(key -> Future.<String>future(s -> redisClient.srandmember(key, s))) // 从 set 随机返回一个对象
            .setHandler(res -> {
                if (res.succeeded()) {
                    message.reply(res.result());
                } else {
                    if (res.cause() instanceof ReplyException) {
                        ReplyException exception = (ReplyException) res.cause();
                        message.fail(exception.failureCode(), exception.getMessage());
                    } else {
                        message.fail(500, res.cause().getMessage());
                    }
                }
            });
    }

    /**
     * @param categories 用户请求的类别 [img, shenghuo ,buyi]
     * @return 返回一个随机类别的 key (set)
     */
    private Future<String> checkAndGetKey(JsonArray categories) {
        Future<String> result = Future.future();
        List<String> toRandom = keysInRedis.getKeys(categories);
        if (toRandom.size() >= 1) {
            result.complete(toRandom.get(random.nextInt(toRandom.size())));
        } else {
            result.fail(new ReplyException(ReplyFailure.RECIPIENT_FAILURE, 404, "没有结果,请检查API"));
        }
        return result;
    }
}