package com.lanyuanxiaoyao.service.launcher.compaction.controller;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.lanyuanxiaoyao.service.common.Constants;
import com.lanyuanxiaoyao.service.common.entity.RunMeta;
import com.lanyuanxiaoyao.service.common.entity.SyncState;
import com.lanyuanxiaoyao.service.configuration.utils.QueueUtil;
import com.lanyuanxiaoyao.service.forest.service.InfoService;
import com.lanyuanxiaoyao.service.forest.service.ZookeeperService;
import com.lanyuanxiaoyao.service.launcher.configuration.ClusterConfiguration;
import java.io.IOException;
import java.util.EnumSet;
import javax.annotation.PreDestroy;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.YarnApplicationState;
import org.apache.hadoop.yarn.client.api.YarnClient;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 提供和集群绑定的一些操作
 *
 * @author ZhangJiacheng
 * @date 2023-05-30
 */
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RestController
@RequestMapping("launcher/compaction")
public class CompactionController {
    private static final Logger logger = LoggerFactory.getLogger(CompactionController.class);

    private final DiscoveryClient discoveryClient;
    private final ClusterConfiguration clusterConfiguration;
    private final InfoService infoService;
    private final ZookeeperService zookeeperService;
    private final YarnClient yarnClient;

    public CompactionController(DiscoveryClient discoveryClient, ClusterConfiguration clusterConfiguration, InfoService infoService, ZookeeperService zookeeperService) {
        this.discoveryClient = discoveryClient;
        this.clusterConfiguration = clusterConfiguration;
        this.infoService = infoService;
        this.zookeeperService = zookeeperService;

        yarnClient = YarnClient.createYarnClient();
        yarnClient.init(new Configuration());
        yarnClient.start();
    }

    @PreDestroy
    public void destroy() throws IOException {
        if (ObjectUtil.isNotNull(yarnClient)) {
            yarnClient.stop();
            yarnClient.close();
        }
    }

    @GetMapping("stop")
    public void stop(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam(value = "disable_meta", defaultValue = "true") Boolean disableMeta
    ) {
        logger.info("Enter method: stop[flinkJobId, alias]. " + "flinkJobId:" + flinkJobId + "," + "alias:" + alias);
        if (disableMeta) {
            logger.info("Delete table info");
            infoService.disableTable(flinkJobId, alias);
        }
        QueueUtil.remove(discoveryClient, clusterConfiguration.getCompactionQueueName(), StrUtil.format("{}-{}", flinkJobId, alias));
        // 再删除一次，避免时间差导致任务重新放回预调度队列
        QueueUtil.remove(discoveryClient, Constants.COMPACTION_QUEUE_PRE, StrUtil.format("{}-{}", flinkJobId, alias));
        logger.info("Remove job form queue");
        try {
            RunMeta meta = zookeeperService.getSyncRunMeta(flinkJobId, alias);
            if (ObjectUtil.isNotNull(meta) && ObjectUtil.isNotEmpty(meta.getApplicationId())) {
                String applicationId = meta.getApplicationId();
                stopApp(applicationId);
                logger.info("Found application id and kill: {}", applicationId);
            } else {
                SyncState syncState = infoService.syncStateDetail(flinkJobId, alias);
                if (ObjectUtil.isNotEmpty(syncState.getCompactionApplicationId())) {
                    String applicationId = syncState.getCompactionApplicationId();
                    stopApp(applicationId);
                    logger.info("Found application id and kill: {}", applicationId);
                }
            }
        } catch (Throwable throwable) {
            logger.warn("Application id not found, it may stop yet");
        }
    }

    @GetMapping("stop_app")
    public void stopApp(@RequestParam("application_id") String applicationId) throws IOException, YarnException {
        boolean exists = yarnClient.getApplications(EnumSet.of(YarnApplicationState.RUNNING))
                .stream()
                .anyMatch(r -> StrUtil.equals(r.getApplicationId().toString(), applicationId));
        if (exists) {
            yarnClient.killApplication(ApplicationId.fromString(applicationId));
        } else {
            logger.info("Application: {} not running", applicationId);
        }
    }
}
