package com.lanyuanxiaoyao.service.yarn.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lanyuanxiaoyao.service.configuration.entity.yarn.YarnApplication;
import com.lanyuanxiaoyao.service.configuration.entity.yarn.YarnClusters;
import com.lanyuanxiaoyao.service.yarn.response.ApplicationDetailResponse;
import com.lanyuanxiaoyao.service.yarn.response.ApplicationsListResponse;
import com.lanyuanxiaoyao.service.yarn.service.JobService;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.list.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

/**
 * Job
 *
 * @author lanyuanxiaoyao
 * @date 2023-04-23
 */
@CacheConfig(cacheManager = "normal-cache")
@Service("JobService")
public class JobServiceImpl implements JobService {
    private static final Logger logger = LoggerFactory.getLogger(JobServiceImpl.class);
    private static final ImmutableList<String> RUNNING_STATE = Lists.immutable.of(YarnApplication.State.RUNNING);
    private static final ImmutableList<String> NOT_RUNNING_STATE = Lists.immutable.of(
            YarnApplication.State.NEW,
            YarnApplication.State.NEW_SAVING,
            YarnApplication.State.SUBMITTED,
            YarnApplication.State.ACCEPTED,
            YarnApplication.State.FINISHED,
            YarnApplication.State.FAILED,
            YarnApplication.State.KILLED
    );
    private final YarnClusters yarnClusters;
    private final ObjectMapper mapper;

    public JobServiceImpl(YarnClusters yarnClusters, Jackson2ObjectMapperBuilder builder) {
        this.yarnClusters = yarnClusters;

        this.mapper = builder.build();
    }

    @Cacheable(value = "job-list", sync = true, key = "#methodName+#cluster")
    @Retryable(Throwable.class)
    @Override
    public ImmutableList<YarnApplication> list(String cluster) throws JsonProcessingException {
        return list(cluster, null);
    }

    @Cacheable(value = "job-list", sync = true, key = "#methodName+#cluster")
    @Retryable(Throwable.class)
    @Override
    public ImmutableList<YarnApplication> listRunning(String cluster) throws JsonProcessingException {
        return list(cluster, RUNNING_STATE);
    }

    @Cacheable(value = "job-list", sync = true, key = "#methodName+#cluster")
    @Retryable(Throwable.class)
    @Override
    public ImmutableList<YarnApplication> listNotRunning(String cluster) throws JsonProcessingException {
        return list(cluster, NOT_RUNNING_STATE);
    }

    public ImmutableList<YarnApplication> list(String cluster, ImmutableList<String> states) throws JsonProcessingException {
        boolean isFilter = ObjectUtil.isNotEmpty(states);
        String queryUrl = URLUtil.completeUrl(yarnClusters.getWebUrl(cluster), "/ws/v1/cluster/apps");
        try (HttpResponse response = HttpUtil.createGet(queryUrl).setMaxRedirectCount(10).execute()) {
            return mapper.readValue(response.body(), ApplicationsListResponse.class)
                    .getApps()
                    .getApp()
                    .tap(app -> app.setCluster(cluster))
                    .select(app -> !isFilter || states.contains(app.getState()));
        }
    }

    @Cacheable(value = "job-list", sync = true, key = "#methodName+#cluster+#name")
    @Retryable(Throwable.class)
    @Override
    public ImmutableList<YarnApplication> listEquals(String cluster, String name) throws JsonProcessingException {
        return listEquals(cluster, null, name);
    }

    @Cacheable(value = "job-list", sync = true, key = "#methodName+#cluster+#name")
    @Retryable(Throwable.class)
    @Override
    public ImmutableList<YarnApplication> listRunningEquals(String cluster, String name) throws JsonProcessingException {
        return listEquals(cluster, RUNNING_STATE, name);
    }

    @Cacheable(value = "job-list", sync = true, key = "#methodName+#cluster+#name")
    @Retryable(Throwable.class)
    @Override
    public ImmutableList<YarnApplication> listNotRunningEquals(String cluster, String name) throws JsonProcessingException {
        return listEquals(cluster, NOT_RUNNING_STATE, name);
    }

    public ImmutableList<YarnApplication> listEquals(String cluster, ImmutableList<String> states, String name) throws JsonProcessingException {
        return list(cluster, states).select(app -> StrUtil.equals(app.getName(), name));
    }

    @Cacheable(value = "job-list", sync = true, key = "#methodName+#cluster+#text")
    @Retryable(Throwable.class)
    @Override
    public ImmutableList<YarnApplication> listLike(String cluster, String text) throws JsonProcessingException {
        return listLike(cluster, null, text);
    }

    @Cacheable(value = "job-list", sync = true, key = "#methodName+#cluster+#text")
    @Retryable(Throwable.class)
    @Override
    public ImmutableList<YarnApplication> listRunningLike(String cluster, String text) throws JsonProcessingException {
        return listLike(cluster, RUNNING_STATE, text);
    }

    @Cacheable(value = "job-list", sync = true, key = "#methodName+#cluster+#text")
    @Retryable(Throwable.class)
    @Override
    public ImmutableList<YarnApplication> listNotRunningLike(String cluster, String text) throws JsonProcessingException {
        return listLike(cluster, NOT_RUNNING_STATE, text);
    }

    public ImmutableList<YarnApplication> listLike(String cluster, ImmutableList<String> states, String text) throws JsonProcessingException {
        return list(cluster, states).select(app -> StrUtil.contains(app.getName(), text));
    }

    @Cacheable(value = "job-detail", sync = true)
    @Retryable(Throwable.class)
    @Override
    public YarnApplication detail(String cluster, String applicationId) throws JsonProcessingException {
        String queryUrl = URLUtil.completeUrl(yarnClusters.getWebUrl(cluster), "/ws/v1/cluster/apps/" + applicationId);
        try (HttpResponse response = HttpUtil.createGet(queryUrl).setMaxRedirectCount(10).execute()) {
            return mapper.readValue(response.body(), ApplicationDetailResponse.class).getApp().setCluster(cluster);
        }
    }
}
