package com.hp.ucmdb.rest.uiserver.integrationstudio.controllers.sample;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/*
    This scenario is to run full sync and then delta sync of all jobs under inactive integration points.
*/
public class JobSyncScenarioSample {
    //the parameters you need to provide are serverIp, userName and password
    public static void main(String[] args) throws Exception {

        //get token for authentication
        String serverIp = "ucmdb-11-2-40.u.cms-demo.com";//input
        String userName = "admin";//input
        String password = "admin";//input
        String token = RestApiConnectionUtils.loginServer(serverIp, userName, password);//login server
        if(token == null || token.length() == 0){
            System.out.println("Can not log in to the UCMDB server. Check your serverIp, userName or password!");
            return;
        }
        System.out.println(token);

        //get details of all integration points
        String getResult = RestApiConnectionUtils.getAllIntegrationPoints(token, serverIp);
        JSONObject allIntegrationPoints = new JSONObject(getResult);
        if(allIntegrationPoints == null){
            System.out.println("Can not get details of all integration points!");
            return;
        }

        //get names of all inactive integration points
        List < String> inactiveList = getAllInactiveIntegrationPoints(allIntegrationPoints);
        if(inactiveList.size() == 0){
            System.out.println("No inactive integration point found!");
            return;
        }
        System.out.println("The list of inactive integration points is " + inactiveList.toString());

        System.out.println();
        System.out.println("Now, start to full sync!");

        //full sync all the jobs of inactive integration points
        for (String integration_name: inactiveList) {
            //activate an inactive integration point
            if(!activeIntegrationPoint(token, serverIp, integration_name)){
                System.out.println("Failed to activate the " + integration_name + " integration point.");
                inactiveList.remove(integration_name);// remove the integration point if activation failed
                continue;
            }

            //get all the job in this integration point
            List < Map < String,String>> jobList = getAllJobs(allIntegrationPoints, integration_name);

            if(jobList.size() > 0){
                syncAllJobs(jobList,token,serverIp,integration_name,"FULL");//"FULL" means full sync
            }else{
                System.out.println("No jobs found in the " + integration_name + " integration point.");
            }
        }

        System.out.println();
        System.out.println("Now, start to delta sync!");

        //delta sync all the jobs of inactive
        for (String integration_name: inactiveList) {

            //get all the jobs in this integration point
            List < Map < String,String>> jobList = getAllJobs(allIntegrationPoints, integration_name);

            if(jobList.size() > 0){
                syncAllJobs(jobList,token,serverIp,integration_name,"DELTA");//"DELTA" means delta sync
            }else{
                System.out.println("No jobs found in the " + integration_name + " integration point.");
            }
        }
        System.out.println("Done!");
    }

    //activate one inactive integration point
    private static boolean activeIntegrationPoint(String token, String serverIP, String integrationPoint_name)throws IOException, JSONException, NoSuchAlgorithmException, KeyManagementException, InterruptedException{

        String result = RestApiConnectionUtils.activateOrDeactivateIntegrationPoint(token, serverIP, integrationPoint_name, true);
        boolean tag = false;
        if(result != null && result.indexOf("200") != -1){
        // waiting for the activating process done
            tag = waitingProcessEnd(3, token, serverIP, integrationPoint_name, null, 0);
        }
        if (!tag){
            System.out.println("Failed to activate the " + integrationPoint_name + " integration point.");
        }
        return tag;
    }

    // sync all jobs in one integration point
    /* there are four values for sync_type: PUSH_FULL / PUSH_DELTA / POPULATION_FULL / POPULATION_DELTA*/
    private static void syncAllJobs(List < Map < String,String>> jobList, String token, String serverIP, String integrationPoint_name, String sync_type)throws InterruptedException, JSONException, NoSuchAlgorithmException, KeyManagementException, IOException{
        for (Map < String,String> map: jobList) {
            String jobCategory = map.get("category");

            // sync population jobs
            if("POPULATION".equals(jobCategory)){
                String job_name = map.get("name");

                //sync the job
                String operation_type = "POPULATION_" + sync_type;
                System.out.println("full population job syncing...");
                String result = RestApiConnectionUtils.syncJob(token, serverIP, integrationPoint_name, job_name, operation_type);
                System.out.println("the request of population " + sync_type + " sync is " + result);

                //waiting for the syncing process done
                if(!waitingProcessEnd(3, token, serverIP, integrationPoint_name, job_name, 1)){
                    System.out.println("Can not get the sync job status! The job name is (" + job_name + ") and integration name is (" + integrationPoint_name + ")");
                    continue;
                }
            }else if("PUSH".equals(jobCategory)){//sync push job
                String job_name = map.get("name");

                //sync the job
                String operation_type = "PUSH_"+ sync_type;
                System.out.println("full push job syncing...");
                String result = RestApiConnectionUtils.syncJob(token, serverIP, integrationPoint_name, job_name, operation_type);
                System.out.println("the result of push " + sync_type + " sync is " + result);

                //waiting for the syncing process done
                if(!waitingProcessEnd(3, token, serverIP, integrationPoint_name, job_name, 1)){
                    System.out.println("Can not get the sync job status! The job name is (" + job_name + ") and integration name is (" + integrationPoint_name + ")");
                    continue;
                }

            }
        }
    }

    //waiting for the activating process or syncing process (interval in seconds)
    private static boolean waitingProcessEnd(int intervalSeconds, String token, String serverIP, String integrationPoint_name, String job_name, int job_category)throws JSONException,IOException{
        boolean loop = false;
        int count = 0;
        while (!loop){
            try{
                Thread.sleep(intervalSeconds*1000);
                count++;
                String printInfo = job_name == null ? "integration point name is " + integrationPoint_name : "integration point name is " + integrationPoint_name +" and job name is " + job_name;
                System.out.println("Checking status. Attempt " + count + " ..." + " (" + printInfo + ")");

                String status = job_name == null ? getIntegrationPointActiveStatus(token, serverIP, integrationPoint_name) : getJobStatus(token, serverIP, integrationPoint_name, job_name, job_category);
                System.out.println("The status is " + status);
                if(status == null){
                    System.out.println("Check the server connection!");
                    break;
                }

                if( status.indexOf("SUCCESS") != -1 || status.indexOf("SUCESS") != -1){
                    System.out.println("Checking over with successful!");
                    System.out.println();
                    loop = true;
                    break;
                }
                if(count >= 20){
                    System.out.println("Checking status failed. You have exceeded the maximum attempts allowed.");
                    break;
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        return loop;
    }

    //get the integration point activation status
    private static String getIntegrationPointActiveStatus(String token, String serverIP, String integrationPoint_name)throws IOException, JSONException{
        //get the special integration point
        String integrationPointDetail = RestApiConnectionUtils.getSingleIntegrationPoint(token,serverIP,integrationPoint_name);

        //get special job in one integration point
        JSONObject integrationJson = new JSONObject(integrationPointDetail);
        String result = integrationJson.getBoolean("enabled") ? "SUCCESS" : "DISABLE";
        return result;
    }


    //get the running job's status
    private static String getJobStatus(String token, String serverIP, String integrationPoint_name, String job_name, int job_category)throws IOException, JSONException{
        //get the specific integration point
        String integrationPointDetail = RestApiConnectionUtils.getSingleIntegrationPoint(token, serverIP, integrationPoint_name);

        if(integrationPointDetail != null){
            String status = null;
            if(job_category == 1)
                status = getPopulationJobStatus(new JSONObject(integrationPointDetail), job_name);//get statuses of population jobs
            if(job_category == 2)
                status = getPushJobStatus(new JSONObject(integrationPointDetail), job_name);//get statuses of push jobs
            if(status == null){
                System.out.println("Failed to get statuses of jobs!");
                return null;
            }
            return status;
        }
        return null;
    }

    //get statuses of population jobs
    private static String getPopulationJobStatus(JSONObject integrationDetail, String job_name)throws JSONException{
        if(integrationDetail != null && integrationDetail.has("dataPopulationJobs")){
            JSONArray populationJobList = integrationDetail.getJSONArray("dataPopulationJobs");
            for(int i = 0; i < populationJobList.length(); i++){
                JSONObject tmp = populationJobList.getJSONObject(i);
                if(tmp.getString("displayID").equals(job_name));
                JSONObject statusJson = tmp.getJSONObject("jobStatistics");
                return statusJson.getString("jobStatus");
            }
        }
        return null;
    }

    //get statuses of push jobs
    private static String getPushJobStatus(JSONObject integrationDetail, String job_name)throws JSONException{
        if(integrationDetail != null && integrationDetail.has("dataPushJobs")){
            JSONArray pushJobList = integrationDetail.getJSONArray("dataPushJobs");
            for (int i = 0; i < pushJobList.length(); i++) {
                JSONObject tmp = pushJobList.getJSONObject(i);
                if(tmp.getString("displayID").equals(job_name)){
                    String status = "UNKNOWN";
                    boolean enabled = integrationDetail.getBoolean("enabled");
                    if(!enabled){//disabled
                        status = "DISABLED";
                    }else {
                        JSONObject statusJson = tmp.getJSONObject("jobRunCurrentStatus");
                        if(statusJson != null && "RUNNING".equals(statusJson.getString("status"))){//running
                            status = "RUNNING";
                        }else {//other status
                            statusJson = tmp.getJSONArray("jobRunHistory").getJSONObject(0);
                            if(statusJson != null) status = statusJson.getString("status");
                        }
                    }
                    return status;
                }
            }
        }
        return null;
    }

    private static List < String> getAllInactiveIntegrationPoints(JSONObject allIntegrationPoints) throws JSONException{
        List < String> list = new ArrayList < >();
        if (allIntegrationPoints != null) {
            Iterator iter = allIntegrationPoints.keys();
            while (iter.hasNext()) {
                String integrationPoint = (String)iter.next();
                if(!"HistoryDataSource".equals(integrationPoint) && !"UCMDBDiscovery".equals(integrationPoint)
                        && !allIntegrationPoints.getJSONObject(integrationPoint).getBoolean("enabled")){
                    list.add(integrationPoint);
                }
            }
        }
        return list;
    }

    private static List < Map < String,String>> getAllJobs(JSONObject allIntegrationPoints, String integrationPoint_name)throws JSONException{
        List < Map < String,String>> jobList = new ArrayList < >();
        JSONObject singleIntegration = allIntegrationPoints.getJSONObject(integrationPoint_name);
        if(singleIntegration == null) return null;

        //add population jobs
        JSONArray populationJobArray = singleIntegration.getJSONArray("dataPopulationJobs");
        for(int i = 0; i < populationJobArray.length(); i++){
            Map tmp = new HashMap <>();
            String jobName = populationJobArray.getJSONObject(i).getString("displayID");
            tmp.put("name",jobName);
            tmp.put("category","POPULATION");
            jobList.add(tmp);
        }

        //add push jobs
        JSONArray pushJobArray = singleIntegration.getJSONArray("dataPushJobs");
        for(int i = 0; i < pushJobArray.length(); i++){
            Map tmp = new HashMap <>();
            String jobName = pushJobArray.getJSONObject(i).getString("displayID");
            tmp.put("name",jobName);
            tmp.put("category","PUSH");
            jobList.add(tmp);
        }

        return jobList;
    }



}