package com.purui.prims.dscheduler.service;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.purui.prims.common.config.DolphinSchedulerConfig;
import com.purui.prims.common.pojo.ResultData;
import com.purui.prims.headless.api.pojo.enums.DolphineSchedulerType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

/**
 * DolphinScheduler基础类
 *
 * @author charlie
 * @date 2024/7/11
 */
@Slf4j
public abstract class AbstractSchedulerService {

    static ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.registerModule(new JavaTimeModule()); // 注册JSR310模块以处理LocalDateTime等类型
    }

    protected final DolphinSchedulerConfig config;

    private final RestTemplate restTemplate;

    public AbstractSchedulerService(DolphinSchedulerConfig config, RestTemplate restTemplate) {
        this.config = config;
        this.restTemplate = restTemplate;
    }

    private <T> T convertResult(ResultData data, Class<T> clazz) throws Exception {
        if (data.getCode() == 0 && data.getData() != null) {
            String json = JSON.toJSONString(data.getData());
            return objectMapper.readValue(json, clazz);
        }
        log.info("调用返回:" + JSON.toJSONString(data));
        return null;
    }

    private <T> List<T> convertResultList(ResultData data, Class clazz) throws Exception {
        if (data.getCode() == 0 && data.getData() != null) {
            String json = JSON.toJSONString(data.getData());
            if (data.getData() instanceof List) {
                return convertJsonToList(json, clazz);
            }
        }
        return null;
    }

    private <T> List<T> convertJsonToList(String json, Class<T> clazz) throws Exception {
        JavaType type = objectMapper.getTypeFactory().constructCollectionType(List.class, clazz);
        return objectMapper.readValue(json, type);
    }

    protected <T> T runSchedulerX(Long projectCode, Long code, DolphineSchedulerType schedulerType, Map<String, Object> data,
                                  Class<T> responseTypeClass) {
        ParameterizedType parameterizedType = new ParameterizedType() {
            @Override
            public Type[] getActualTypeArguments() {
                return new Type[] {responseTypeClass};
            }

            @Override
            public Type getRawType() {
                return ResultData.class;
            }

            @Override
            public Type getOwnerType() {
                return null;
            }
        };

        ParameterizedTypeReference<ResultData<T>> responseType =
            new ParameterizedTypeReference<ResultData<T>>() {
                @Override
                public Type getType() {
                    return parameterizedType;
                }
            };

        ResultData<T> resultData = runScheduler(projectCode, code, schedulerType, data, responseType);
        try {
            return convertResult(resultData, responseTypeClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    protected <T> List<T> runSchedulerListX(Long projectCode, Long code, DolphineSchedulerType schedulerType, Map<String, Object> data,
                                            Class<T> responseTypeClass) {
        ParameterizedType parameterizedType = new ParameterizedType() {
            @Override
            public Type[] getActualTypeArguments() {
                return new Type[] {new ParameterizedTypeReference<List<T>>() {
                }.getType()};
            }

            @Override
            public Type getRawType() {
                return ResultData.class;
            }

            @Override
            public Type getOwnerType() {
                return null;
            }
        };

        ParameterizedTypeReference<ResultData<List<T>>> responseType =
            new ParameterizedTypeReference<ResultData<List<T>>>() {
                @Override
                public Type getType() {
                    return parameterizedType;
                }
            };
        ResultData<List<T>> resultData = runScheduler(projectCode, code, schedulerType, data, responseType);
        try {
            return convertResultList(resultData, responseTypeClass);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return null;
    }

    private <T> ResultData<T> runScheduler(Long projectCode, Long code, DolphineSchedulerType schedulerType, Map<String, Object> data,
                                           ParameterizedTypeReference<ResultData<T>> responseType) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("token", config.getToken());
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<MultiValueMap<String, String>> requestEntity = null;

        String url = getRequestURL(projectCode, code, schedulerType);
        HttpMethod method = HttpMethod.valueOf(schedulerType.getMethod());

        MultiValueMap<String, String> dataMap = new LinkedMultiValueMap<>();

        String params = "";
        if (data != null) {
            try {
                for (Map.Entry<String, Object> entry : data.entrySet()) {
                    if (HttpMethod.GET.equals(method)) {
                        params += entry.getKey() + "=" + URLEncoder.encode(String.valueOf(entry.getValue()), "utf-8") + "&";
                    } else {
                        dataMap.add(entry.getKey(), String.valueOf(entry.getValue()));
                    }
                }
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }

        if (StringUtils.isNotEmpty(params)) {
            url = String.format("%s?%s", url, params);
        }
        requestEntity = new HttpEntity<>(dataMap, headers);
        log.info("request url: {},params :{}, method: {}", url, dataMap, method);

        ResponseEntity<ResultData<T>> response = restTemplate.exchange(URI.create(url), method, requestEntity, responseType);
        if (response.getStatusCode() == HttpStatus.OK) {
            return response.getBody();
        }
        return ResultData.fail(response.getStatusCode().value(), response.getBody().getMsg());
    }

    private String getRequestURL(Long projectCode, DolphineSchedulerType schedulerType) {
        String url = config.getUrl() + schedulerType.getUri();
        url = url.replace("{projectCode}", projectCode.toString());
        return url;
    }

    private String getRequestURL(Long projectCode, Long code, DolphineSchedulerType schedulerType) {
        String url = getRequestURL(projectCode, schedulerType);
        url = url.replace("{code}", code.toString());
        url = url.replace("{id}", code.toString());
        return url;
    }
}
