package com.lanyuanxiaoyao.service.queue;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lanyuanxiaoyao.service.configuration.entity.queue.QueueItem;
import com.lanyuanxiaoyao.service.queue.configuration.AutoSaveProperties;
import com.lanyuanxiaoyao.service.queue.configuration.Constants;
import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.PriorityBlockingQueue;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 启动类
 *
 * @author lanyuanxiaoyao
 * @date 2023-05-04
 */
@EnableDiscoveryClient
@EnableConfigurationProperties
@EnableEncryptableProperties
@SpringBootApplication(scanBasePackages = {"com.lanyuanxiaoyao.service"})
@EnableScheduling
@RestController
@RequestMapping("queue")
public class QueueApplication implements ApplicationRunner {
    private static final Logger logger = LoggerFactory.getLogger(QueueApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(QueueApplication.class, args);
    }

    @Resource
    private AutoSaveProperties autoSaveProperties;
    @Resource
    private Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder;
    @Resource
    private DiscoveryClient client;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 队列不支持分布式启动，只能启动一个
        if (client.getServices().contains("queue")) {
            logger.warn("Wait for 15s");
            Thread.sleep(15000);
            logger.error("Found queue exists, exit...");
            System.exit(1);
        }

        if (autoSaveProperties.getLocation() != null && !"".equals(autoSaveProperties.getLocation())) {
            ObjectMapper mapper = jackson2ObjectMapperBuilder.build();

            String dequeData = load(Constants.DEQUE_SAVE_FILENAME);
            if (Objects.nonNull(dequeData)) {
                ConcurrentHashMap<String, ConcurrentLinkedDeque<QueueItem<?>>> map = mapper.readValue(dequeData, new TypeReference<ConcurrentHashMap<String, ConcurrentLinkedDeque<QueueItem<?>>>>() {
                });
                Constants.DEQUE_MAP.putAll(map);
            }

            String queueData = load(Constants.QUEUE_SAVE_FILENAME);
            if (Objects.nonNull(queueData)) {
                ConcurrentHashMap<String, PriorityBlockingQueue<QueueItem<?>>> map = mapper.readValue(queueData, new TypeReference<ConcurrentHashMap<String, PriorityBlockingQueue<QueueItem<?>>>>() {
                });
                Constants.QUEUE_MAP.putAll(map);
            }

            // 如果启用了自动保存，数据加载完成后才开始自动保存
            if (autoSaveProperties.getEnable()) {
                Constants.AUTO_SAVE.set(true);
            } else {
                logger.warn("Auto save is disabled");
            }
        } else {
            logger.warn("Save path is not set");
        }
    }

    private String load(String name) throws IOException {
        Path saveDataPath = Paths.get(autoSaveProperties.getLocation(), name);
        if (Files.exists(saveDataPath)) {
            String data = new String(Files.readAllBytes(saveDataPath));
            data = URLDecoder.decode(data, "utf-8");
            return data;
        }
        logger.warn("Cannot found any data from {}", saveDataPath);
        return null;
    }
}
