package cn.wise.sc.energy.power.plant.business.opentsdb;

import cn.wise.sc.energy.power.plant.business.domain.CharacterParamInfo;
import cn.wise.sc.energy.power.plant.business.domain.vo.CharacterParamInfoVo;
import cn.wise.sc.energy.power.plant.business.opentsdb.bean.PutRequestExt;
import cn.wise.sc.energy.power.plant.business.opentsdb.bean.QueryRequestExt;
import cn.wise.sc.energy.power.plant.business.utils.OkHttpClientUtil;
import com.alibaba.fastjson.JSON;
import net.opentsdb.client.api.Endpoint;
import net.opentsdb.client.api.annotation.request.CreateAnnotationRequest;
import net.opentsdb.client.api.annotation.request.DeleteAnnotationRequest;
import net.opentsdb.client.api.annotation.request.GetAnnotationRequest;
import net.opentsdb.client.api.annotation.request.UpdateAnnotationRequest;
import net.opentsdb.client.api.annotation.response.CreateAnnotationResponse;
import net.opentsdb.client.api.annotation.response.DeleteAnnotationResponse;
import net.opentsdb.client.api.annotation.response.GetAnnotationResponse;
import net.opentsdb.client.api.annotation.response.UpdateAnnotationResponse;
import net.opentsdb.client.api.delete.request.DeleteRequest;
import net.opentsdb.client.api.delete.response.DeleteResponse;
import net.opentsdb.client.api.put.request.PutRequest;
import net.opentsdb.client.api.put.response.PutResponse;
import net.opentsdb.client.api.query.request.QueryLastRequest;
import net.opentsdb.client.api.query.request.QueryRequest;
import net.opentsdb.client.api.query.response.QueryLastResponse;
import net.opentsdb.client.api.query.response.QueryResponse;
import net.opentsdb.client.api.suggest.request.SuggestRequest;
import net.opentsdb.client.api.suggest.response.SuggestResponse;
import net.opentsdb.client.api.uid.request.UIDAssignRequest;
import net.opentsdb.client.api.uid.response.UIDAssignResponse;
import net.opentsdb.client.bean.Annotation;
import net.opentsdb.client.bean.LastDataPoint;
import net.opentsdb.client.bean.LastDataPointQuery;
import net.opentsdb.client.bean.QueryResult;
import net.opentsdb.client.util.JsonUtil;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * opentsdb工具类
 *
 * @author neo.shu
 * @since 2020/4/28 15:41
 */
@Component
public class OpentsdbOkHttpClient {

    public static final String BASE_URL = "http://39.105.86.33:8182";
//    public static final String BASE_URL = "http://192.168.1.130:8182";
/*    @Value("${opentsdb}")
    public String BASE_URL = "http://39.105.86.33:8182";*/

    /**
     * /api/uid/assign
     * <br/><br/>
     * This endpoint enables assigning UIDs to new metrics, tag names and tag values.
     * Multiple types and names can be provided in a single call
     * and the API will process each name individually,
     * reporting which names were assigned UIDs successfully, along with the UID assigned,
     * and which failed due to invalid characters or had already been assigned.
     * Assignment can be performed via query string or content data.
     *
     * @param request UIDAssignRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/uid/assign.html">/api/uid/assign</a>
     */
    public UIDAssignResponse uidAssign(UIDAssignRequest request)
            throws IOException, URISyntaxException {
        String body = OkHttpClientUtil.getInstance().postJson(BASE_URL + Endpoint.UID_ASSIGN.getPath(), JsonUtil.writeValueAsString(request));
        UIDAssignResponse result = JsonUtil.readValue(body, UIDAssignResponse.class);
        result.setRequestUUID(request.getRequestUUID());
        return result;
    }

    /**
     * /api/put
     * <br/><br/>
     * This endpoint allows for storing data in OpenTSDB over HTTP
     * as an alternative to the Telnet interface.
     * Put requests can only be performed via content associated with the POST method.
     * The format of the content is dependent on the serializer selected.
     * However there are some common parameters and responses as documented below.
     *
     * @param request PutRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/put.html">/api/put</a>
     */
    public PutResponse put(PutRequestExt request) throws IOException, URISyntaxException {
        String body = OkHttpClientUtil.getInstance().postJson(BASE_URL + Endpoint.PUT.getPath(), JsonUtil.writeValueAsString(request.getDataPoints()), request.getParameters());
        PutResponse result = JsonUtil.readValue(body, PutResponse.class);
        // empty response
        if (result == null) {
            result = PutResponse.builder()
                    .success(request.getDataPoints().size())
                    .failed(0)
                    .errors(new LinkedList<>())
                    .build();
        }
        result.setRequestUUID(request.getRequestUUID());
        return result;
    }

    /**
     * /api/query
     * <br/><br/>
     * Probably the most useful endpoint in the API,
     * /api/query enables extracting data from the storage system in various formats
     * determined by the serializer selected.
     * Queries can be submitted via the 1.0 query string format or body content.
     *
     * @param request QueryRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/query/index.html">/api/query</a>
     */
    public static QueryResponse query(QueryRequestExt request) throws IOException, URISyntaxException {
        String a = JsonUtil.writeValueAsString(request);
        String response = OkHttpClientUtil.getInstance().postJson(BASE_URL + Endpoint.QUERY.getPath(), JsonUtil.writeValueAsString(request));
        List<QueryResult> results = JsonUtil.readValue(response, List.class, QueryResult.class);

        return QueryResponse.builder()
                .requestUUID(request.getRequestUUID())
                .results(results)
                .build();
    }

    /**
     * /api/query
     * <br/><br/>
     * As of 2.2 data matching a query can be deleted by using the DELETE verb.
     * The configuration parameter {@code tsd.http.query.allow_delete} must be enabled to allow deletions.
     * Data that is deleted will be returned in the query results.
     * Executing the query a second time should return empty results.
     * <br/><br/>
     * WARNING:
     * Deleting data is permanent. Also beware that when deleting,
     * some data outside the boundaries of the start and end times may be deleted
     * as data is stored on an hourly basis.
     *
     * @param request DeleteRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/query/index.html">/api/query</a>
     */
    public DeleteResponse delete(DeleteRequest request) throws IOException, URISyntaxException {
        String response = OkHttpClientUtil.getInstance().postJson(BASE_URL + Endpoint.QUERY.getPath(), JsonUtil.writeValueAsString(request));
        List<QueryResult> results = JsonUtil.readValue(response, List.class, QueryResult.class);
        return DeleteResponse.builder()
                .requestUUID(request.getRequestUUID())
                .results(results)
                .build();
    }

    /**
     * /api/suggest
     * <br/><br/>
     * <p>
     * This endpoint provides a means of implementing an "auto-complete" call
     * that can be accessed repeatedly as a user types a request in a GUI.
     * It does not offer full text searching or wildcards,
     * rather it simply matches the entire string passed in the query
     * on the first characters of the stored data.
     * For example, passing a query of type=metrics&q=sys
     * will return the top 25 metrics in the system that start with sys.
     * Matching is case sensitive, so sys will not match System.CPU.
     * Results are sorted alphabetically.
     *
     * @param request SuggestRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/suggest.html">/api/suggest</a>
     */
    public SuggestResponse suggest(SuggestRequest request) throws IOException, URISyntaxException {
        String response = OkHttpClientUtil.getInstance().postJson(BASE_URL + Endpoint.SUGGEST.getPath(), JsonUtil.writeValueAsString(request));
        List<String> results = JsonUtil.readValue(response, List.class, String.class);
        return SuggestResponse.builder()
                .requestUUID(request.getRequestUUID())
                .results(results)
                .build();
    }

    /**
     * /api/query/last <br/><br/>
     * <p>
     * This endpoint (2.1 and later) provides support for accessing the latest value of individual
     * time series. It provides an optimization over a regular query when only the last data point is
     * required. Locating the last point can be done with the timestamp of the meta data counter or by
     * scanning backwards from the current system time.
     *
     * @param request QueryLastRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/query/last.html">/api/suggest</a>
     */
    public static QueryLastResponse queryLast(QueryLastRequest request)
            throws IOException, URISyntaxException {
        String response = OkHttpClientUtil.getInstance().postJson(BASE_URL + Endpoint.QUERY_LAST.getPath(), JsonUtil.writeValueAsString(request));
        List<LastDataPoint> results = JsonUtil.readValue(response, List.class, LastDataPoint.class);
        return QueryLastResponse.builder()
                .requestUUID(request.getRequestUUID())
                .results(results)
                .build();
    }

    public static Long queryLast(List<CharacterParamInfoVo> characterParamInfoVos) {
        QueryLastRequest queryLastRequest = new QueryLastRequest();
        List<LastDataPointQuery> list = new ArrayList<>();
        characterParamInfoVos.stream().forEach(info -> {
            list.add(LastDataPointQuery.builder().metric(info.getKksCode()).build());
        });
        queryLastRequest.setQueries(list);
        QueryLastResponse queryLastResponse;
        try {
            queryLastResponse = OpentsdbOkHttpClient.queryLast(queryLastRequest);
            List<LastDataPoint> results = queryLastResponse.getResults();
            List<Long> timeList = new ArrayList<>();
            final Long[] latestTime = {0L};
            if (!CollectionUtils.isEmpty(results)) {
                results.stream().forEach(result -> {
                    timeList.add(result.getTimestamp());
                    if (latestTime[0] < result.getTimestamp()) {
                        latestTime[0] = result.getTimestamp();
                    }
                });
                return timeList.get(0);
            } else {
                return 0L;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return 0L;
    }

    /**
     * /api/annotation <br/><br/>
     * <p>
     * These endpoints provides a means of adding, editing or deleting annotations stored in the
     * OpenTSDB backend. Annotations are very basic objects used to record a note of an arbitrary
     * event at some point, optionally associated with a timeseries. Annotations are not meant to be
     * used as a tracking or event based system, rather they are useful for providing links to such
     * systems by displaying a notice on graphs or via API query calls. <br/> When creating, modifying
     * or deleting annotations, all changes will be propagated to the search plugin if configured.
     *
     * @param request CreateAnnotationRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/annotation/index.html">/api/annotation</a>
     */
    public CreateAnnotationResponse createAnnotation(CreateAnnotationRequest request)
            throws IOException, URISyntaxException {
        String response = OkHttpClientUtil.getInstance().postJson(BASE_URL + Endpoint.ANNOTATION.getPath(), JsonUtil.writeValueAsString(request));
        Annotation result = JsonUtil.readValue(response, Annotation.class);
        return CreateAnnotationResponse.builder()
                .requestUUID(request.getRequestUUID())
                .result(result)
                .build();
    }

    /**
     * /api/annotation <br/><br/>
     * <p>
     * These endpoints provides a means of adding, editing or deleting annotations stored in the
     * OpenTSDB backend. Annotations are very basic objects used to record a note of an arbitrary
     * event at some point, optionally associated with a timeseries. Annotations are not meant to be
     * used as a tracking or event based system, rather they are useful for providing links to such
     * systems by displaying a notice on graphs or via API query calls. <br/> When creating, modifying
     * or deleting annotations, all changes will be propagated to the search plugin if configured.
     *
     * @param request GetAnnotationRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/annotation/index.html">/api/annotation</a>
     */
    public GetAnnotationResponse getAnnotation(GetAnnotationRequest request)
            throws IOException, URISyntaxException {
        Response response = OkHttpClientUtil.getInstance().getData(BASE_URL + Endpoint.ANNOTATION.getPath(), request.getParameters());
        Annotation result = JsonUtil.readValue(response.body().string(), Annotation.class);
        return GetAnnotationResponse.builder()
                .requestUUID(request.getRequestUUID())
                .result(result)
                .build();
    }

    /**
     * /api/annotation <br/><br/>
     * <p>
     * These endpoints provides a means of adding, editing or deleting annotations stored in the
     * OpenTSDB backend. Annotations are very basic objects used to record a note of an arbitrary
     * event at some point, optionally associated with a timeseries. Annotations are not meant to be
     * used as a tracking or event based system, rather they are useful for providing links to such
     * systems by displaying a notice on graphs or via API query calls. <br/> When creating, modifying
     * or deleting annotations, all changes will be propagated to the search plugin if configured.
     *
     * @param request UpdateAnnotationRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/annotation/index.html">/api/annotation</a>
     */
    public UpdateAnnotationResponse updateAnnotation(UpdateAnnotationRequest request)
            throws IOException, URISyntaxException {
        String response = OkHttpClientUtil.getInstance().putJson(BASE_URL + Endpoint.ANNOTATION.getPath(), JsonUtil.writeValueAsString(request));
        Annotation result = JsonUtil.readValue(response, Annotation.class);
        return UpdateAnnotationResponse.builder()
                .requestUUID(request.getRequestUUID())
                .result(result)
                .build();
    }

    /**
     * /api/annotation <br/><br/>
     * <p>
     * These endpoints provides a means of adding, editing or deleting annotations stored in the
     * OpenTSDB backend. Annotations are very basic objects used to record a note of an arbitrary
     * event at some point, optionally associated with a timeseries. Annotations are not meant to be
     * used as a tracking or event based system, rather they are useful for providing links to such
     * systems by displaying a notice on graphs or via API query calls. <br/> When creating, modifying
     * or deleting annotations, all changes will be propagated to the search plugin if configured.
     *
     * @param request DeleteAnnotationRequest
     * @throws IOException
     * @throws URISyntaxException
     * @see <a href="http://opentsdb.net/docs/build/html/api_http/annotation/index.html">/api/annotation</a>
     */
    public DeleteAnnotationResponse deleteAnnotation(DeleteAnnotationRequest request)
            throws IOException, URISyntaxException {
        Response response = OkHttpClientUtil.getInstance().deleteData(BASE_URL + Endpoint.ANNOTATION.getPath(), request.getParameters());
        DeleteAnnotationResponse result = JsonUtil.readValue(response.body().string(), DeleteAnnotationResponse.class);
        // empty response
        if (result == null) {
            result = DeleteAnnotationResponse.builder()
                    .build();
        }
        result.setRequestUUID(request.getRequestUUID());
        return result;
    }
}
