package com.hp.ucmdb.rest.uiserver.integrationstudio.controllers.sample;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import javax.net.ssl.*;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class RestApiConnectionUtils {

    public static String loginServer(String serverIP, String userName, String password)throws JSONException, IOException{
        //HTTPS protocol, server IP and API type as the prefix of REST API URL.
        if(serverIP == null || serverIP.length() == 0 || userName == null || userName.length() == 0 || password == null || password.length()== 0){
            System.out.println("please input correct serverIp or userName or password!");
            return null;
        }
        String domainURLAndApiType="https://" + serverIP + ":8443/rest-api/";

        //adminUser has the integration access.
        JSONObject loginJson = new JSONObject();
        loginJson.put("clientContext","1");
        loginJson.put("username",userName);
        loginJson.put("password",password);

        //Put username and password in HTTP request body and invoke REST API(rest-api/authenticate) with POST method to get token.
        System.out.print("login server request : ");
        String result = doPost(domainURLAndApiType+"authenticate", null, loginJson.toString());
        System.out.println("the response of login is " + result);
        if(result == null || result.length() == 0){
            System.out.println("Failed to connect with the UCMDB server!");
            return result;
        }
        String token = new JSONObject(result).getString("token");

        if(token != null) System.out.println("Connect to server Successfully!");
        return token;
    }

    public static String getAllIntegrationPoints(String token, String serverIP)throws IOException{
        String domainURLAndApiType="https://" + serverIP + ":8443/rest-api/";
        System.out.print("getting all integration points request : ");
        String allIntegrationPoints = doGet(domainURLAndApiType + "integration/integrationpoints", token);
        System.out.println( "all integration points details : " + allIntegrationPoints);
        return allIntegrationPoints;
    }

    public static List getAllIntegrationPointNames(JSONObject allIntegrationPoints){
        List allIntegrationPointNames = new ArrayList<>();
        if (allIntegrationPoints != null) {
            Iterator iter = allIntegrationPoints.keys();
            while (iter.hasNext()) {
                Object integrationPoint = iter.next();
                allIntegrationPointNames.add((String)integrationPoint);
            }
        }
        return allIntegrationPointNames;
    }

    public static String getSingleIntegrationPoint(String token, String serverIP, String integrationPoint_name)throws IOException{
        String domainURLAndApiType="https://" + serverIP + ":8443/rest-api/";
        System.out.print("getting the " + integrationPoint_name + " integration point request : ");
        return doGet(domainURLAndApiType + "integration/integrationpoints/" + integrationPoint_name + "?detail=false", token);
    }

    public static String activateOrDeactivateIntegrationPoint(String token, String serverIP, String integrationPoint_name, boolean enabled)throws IOException,JSONException,NoSuchAlgorithmException,KeyManagementException{
        String domainURLAndApiType="https://" + serverIP + ":8443/rest-api/";
        JSONObject activeJson = new JSONObject();
//        activeJson.put("name", integrationPoint_name);
//        activeJson.put("enabled", enabled);// true: means active the integration point, false: means deactive it
        String enableString = enabled ? "true" : "false";
        String activate = enabled ? "activate" : "deactivate";
        System.out.print( activate + " integration point request : ");
        return doPatch(domainURLAndApiType + "integration/integrationpoints/"+ integrationPoint_name + "?enabled=" + enableString, activeJson.toString(), token);
    }

    public static String syncJob(String token, String serverIP, String integrationPoint_name, String job_name, String operation_type)throws IOException,JSONException,NoSuchAlgorithmException,KeyManagementException{
        String domainURLAndApiType="https://" + serverIP + ":8443/rest-api/";

        String job_id = integrationPoint_name + "_" + job_name;
        JSONObject syncJson = new JSONObject();
        syncJson.put("operationType",operation_type);//there are four values for the operation_type parameter: PUSH_FULL / PUSH_DELTA / POPULATION_FULL / POPULATION_DELTA */
        System.out.print("sync job request : ");
        return doPatch(domainURLAndApiType + "integration/integrationpoints/" + integrationPoint_name + "/jobs/" + job_name + "?operationtype=" + operation_type, syncJson.toString(), token);
    }

    public static String testConnectionWithIntegration(String token, String serverIP, String integrationPoint_name)throws IOException{
        String domainURLAndApiType="https://" + serverIP + ":8443/rest-api/";
        System.out.print("test connection with integration point request : ");
        return doGet(domainURLAndApiType + "integration/integrationpoints/" + integrationPoint_name + "/connectionstatus", token);
    }


    public static String doPost(String url, String token, String content)throws IOException {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Content-Type", "application/json;charset=utf-8");
        if(token != null) httpPost.setHeader("Authorization", "Bearer " + token);
        httpPost.setHeader("Accept", "application/json");
        if(content!= null && content.length()>0){
            StringEntity entity = new StringEntity(content);
            httpPost.setEntity(entity);
        }

        SSLContext sslcontext = null;
        try {
            sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            e.printStackTrace();
        }
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext,
                (s, sslSession) -> true);

        CloseableHttpClient c = HttpClients
                .custom()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .setSSLHostnameVerifier(new TrustAnyHostnameVerifier())
                .build();
        CloseableHttpResponse httpResponse = c.execute(httpPost);
        String result = EntityUtils.toString(httpResponse.getEntity());
        System.out.println("POST call returned a status code of " + printStatusCode(httpResponse.getStatusLine().getStatusCode()));
        httpResponse.close();
        return result;
    }

    public static String doGet(String url, String token) throws  IOException {
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Content-Type", "application/json;charset=utf-8");
        httpGet.setHeader("Authorization", "Bearer " + token);
        httpGet.setHeader("Accept", "application/json");

        SSLContext sslcontext = null;
        try {
            sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            e.printStackTrace();
        }
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext,
                (s, sslSession) -> true);

        CloseableHttpClient c = HttpClients
                .custom()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .setSSLHostnameVerifier(new TrustAnyHostnameVerifier())
                .build();
        CloseableHttpResponse httpResponse = c.execute(httpGet);
        String result = EntityUtils.toString(httpResponse.getEntity());
        System.out.println("GET call returned a status code of " + printStatusCode(httpResponse.getStatusLine().getStatusCode()));
        httpResponse.close();
        return result;
    }

    public static String doPatch(String url, String content, String token) throws IOException,NoSuchAlgorithmException, KeyManagementException {

        HttpPatch p = new HttpPatch(url);
        p.setHeader("Content-Type", "application/json;charset=utf-8");
        p.setHeader("Authorization", "Bearer " + token);
        p.setHeader("Accept", "application/json");
        if(content!= null && content.length()>0){
            StringEntity entity = new StringEntity(content);
            p.setEntity(entity);
        }

        SSLContext sslcontext = null;
        try {
            sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            e.printStackTrace();
        }
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext,
                (s, sslSession) -> true);

        CloseableHttpClient c = HttpClients
                .custom()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .setSSLHostnameVerifier(new TrustAnyHostnameVerifier())
                .build();
        CloseableHttpResponse httpResponse = c.execute(p);
        String result = EntityUtils.toString(httpResponse.getEntity());
        System.out.println("PATCH call returned a status code of " + printStatusCode(httpResponse.getStatusLine().getStatusCode()));
        httpResponse.close();
        return result;
    }

    public static String printStatusCode(int status){
        String result = "";
        switch (status){
            case 200:
                result = "200 (Successful)";break;
            case 400:
                result = "400 (Bad Request) Syntax/data provided is not valid for the request";break;
            case 401:
                result = "401 (Unauthorized) User not authorized or invalid session token";break;
            case 403:
                result = "403 (Forbidden) Operation is not allowed (for any user)";break;
            case 404:
                result = "404 (Not Found) The URI points to a non-existent resource/collection";break;
            case 405:
                result = "405 (Method Not Allowed) The HTTP method is not allowed for the resource";break;
            case 406:
                result = "406 (Not Acceptable) Media-type specified in the Accept header is not supported";break;
            case 412:
                result = "412 (Precondition Failed) Start and count values cannot be satisfied in a query";break;
            case 415:
                result = "415 (Unsupported Media Type) Media-type specified in the Content-Type header is not supported";break;
            case 500:
                result = "500 (Internal Server Error) An unexpected/server-side error has occurred";break;
            case 501:
                result = "501 (Not Implemented) The HTTP method is not currently implemented for the given resource/collection URI";break;
            case 503:
                result = "503 (Service Unavailable) The server is currently unavailable";break;

        }
        return result;
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }
}