package com.bocloud.ims.booter.config;

import com.bocloud.cmp.entity.enums.ServerAssetCategory;
import com.bocloud.cmp.entity.enums.installation.InstallStatus;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.common.cache.DictionaryCache;
import com.bocloud.ims.entity.common.Dictionary;
import com.bocloud.ims.entity.resource.Server;
import com.bocloud.ims.interfaces.resource.ServerService;
import com.bocloud.ims.repository.resource.DictionaryRepository;
import com.bocloud.ims.service.resource.server.InstallationServerServiceImpl;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.core.CurrentService;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class SystemInitConfig implements InitializingBean {

    private static Logger logger = LoggerFactory.getLogger(SystemInitConfig.class);
    private static String OPERATION = "node.discover";
    private static boolean running = true;
    @Autowired
    private DictionaryRepository dictionaryRepository;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private CurrentService currentService;
    @Qualifier("ServerService")
    @Autowired
    private ServerService serverService;
    @Autowired
    private InstallationServerServiceImpl installationServerService;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private AmqpTemplate amqpTemplate;

    @Override
    public void afterPropertiesSet() {
        // 缓存字典信息
        taskExecutor.submit(this::dictCache);
        checkDiscoverTimeout();
    }

    private void check() {
        List<Param> params = Lists.newArrayList();
        params.add(new Param(MapTools.simpleMap("assetCategory", ServerAssetCategory.BARE_METAL.name()), Sign.EQ));

        BocloudResult bocloudResult = serverService.list(new Pager(1, Integer.MAX_VALUE, false, params, null));
        if (bocloudResult.isFailed()) {
            logger.warn("查询物理机失败:{}", bocloudResult);
            return;
        }
        GridBean gridBean = (GridBean) bocloudResult.getData();
        List<Server> servers = (List<Server>) gridBean.getRows();
        for (Server node : servers) {
            // 如果当前物理机不被自己管理，不作处理
            if (StringUtils.isNotEmpty(currentService.getService().getInstance())
                    && !currentService.getService().getInstance().equals(node.getInstance())) {
                continue;
            }
            // 通过discover_time是否为null来判断这个机器是否在发现中
            if (node.getDiscoverTime() != null && InstallStatus.DISCOVERYING.getText().equals(node.getStatus())) {
                long start = node.getDiscoverTime().getTime();
                // 机器发现超时
                if (System.currentTimeMillis() - start > 1000 * 60 * 60) {

                    // 界面消息
                    RequestContext context = new RequestContext();
                    context.setTarget(0L);
                    context.setTenant(0L);
                    context.setProject(0L);
                    context.setCatalog(RequestContext.Catalog.Manager);

                    // 更新机器发现状态
                    node.setStatus(InstallStatus.DISCOVERY_FAILED.getText());
                    node.setGmtModify(new Date());
                    node.setMenderId(0L);
                    node.setDiscoverTime(null);
                    BocloudResult result = installationServerService.update(node, context);
                    if (result.isFailed()) {
                        logger.error("检查机器发现是否超时时，请求cmp更新物理机状态失败: {}", result);
                    }
                    String content = "物理机[" + node.getName() + "](ipmi ip=" + node.getConfig().getIpmiAddress()
                            + ")发现超时。请查看物理机控制台，确认机器网络启动是否有问题";
                    logger.warn(content);
                    // 第一个true是发送站内信，第二个true是发送邮件
                    NotifyMessage message = new NotifyMessage(new RequestContext(RequestContext.Catalog.Manager, 1L, null, null, null),
                            "物理主机新增通知", content, "管理员您好，" + content, true, false);
                    // 站内信和邮件发送
                    amqpTemplate.convertAndSend("event.notice.queue", message);
                    OperateResult operateResult = new OperateResult(false, content, OPERATION, null,
                            OperateResult.OperateCategory.MachineDiscovered, context);
                    resourceEventPublisher.send(operateResult);
                }
            }
        }
    }

    private void checkDiscoverTimeout() {
        taskExecutor.execute(() -> {
            while (running) {
                try {
                    check();
                    Thread.sleep(5 * 60 * 1000);
                } catch (Exception e) {
                    logger.error("检查机器发现超时执行异常:", e);
                }
            }
        });
    }

    @PreDestroy
    public void destory() {
        running = false;
    }

    private void dictCache() {
        logger.info("Start to init cache dictionary...");
        try {
            List<Dictionary> dictList = dictionaryRepository.list();
            Map<String, String> itemMap = new ConcurrentHashMap<String, String>(8);
            for (Dictionary dict : dictList) {
                itemMap.put(dict.getValue(), dict.getName());
            }
            synchronized (DictionaryCache.class) {
                DictionaryCache.writeCache(itemMap);
            }
            logger.info("Cache dictionary successful!");
        } catch (Exception e) {
            logger.error("Cache dictionary exception!", e);
            System.exit(-1);
        }
    }
}
