/*******************************************************************************
 * Copyright 2019 T-Mobile USA, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * See the LICENSE file for additional language around disclaimer of warranties.
 * Trademark Disclaimer: Neither the name of "T-Mobile, USA" nor the names of
 * its contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 ******************************************************************************/
package com.tmobile.kardio.surveiller.marathon;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ArrayNode;

import com.tmobile.kardio.surveiller.constants.SurveillerConstants;
import com.tmobile.kardio.surveiller.enums.ComponentType;
import com.tmobile.kardio.surveiller.enums.HealthCheckType;
import com.tmobile.kardio.surveiller.enums.Region;
import com.tmobile.kardio.surveiller.enums.Status;
import com.tmobile.kardio.surveiller.util.CommonsUtil;
import com.tmobile.kardio.surveiller.util.DBQueryUtil;
import com.tmobile.kardio.surveiller.vo.ComponentVO;
import com.tmobile.kardio.surveiller.vo.EnvironmentVO;
import com.tmobile.kardio.surveiller.vo.HealthCheckVO;
import com.tmobile.kardio.surveiller.vo.StatusVO;

/**
 * Class to handle the Marathon configurations
 */
public class MarathonConfigProcessor {
    
    private static final Logger logger = Logger.getLogger(MarathonConfigProcessor.class);
    
    private MarathonConfigProcessor() {}
    
    /**
     * Function to update the API list
     * @param environmentVOs Environment details
     * @throws JsonProcessingException
     * @throws SQLException
     * @throws IOException
     */
    public static void updateApiList(List<EnvironmentVO> environmentVOs) throws JsonProcessingException, SQLException, IOException{
        for(EnvironmentVO eVo : environmentVOs){
            if(eVo.getMarathonJson() != null && !eVo.getMarathonJson().equals("")){
                updateComponentFromMarathonConfig(eVo.getMarathonJson(), eVo.getEnvironmentId(), Region.WEST_REGION.getRegionId());
            }
            if(eVo.getEastMarathonJson() != null && !eVo.getEastMarathonJson().equals("")){
                updateComponentFromMarathonConfig(eVo.getEastMarathonJson(), eVo.getEnvironmentId(), Region.EAST_REGION.getRegionId());
            }
        }
    }
    
    /**
     * Function to update component table from marathon config 
     * @param json Marathon config JSON
     * @param environmentId environment Id
     * @throws JsonProcesssingException
     * @throws IOException
     * @throws SQLException
     */
    @SuppressWarnings("deprecation")
	public static void updateComponentFromMarathonConfig(String json, int environmentId, long regionId) throws SQLException, JsonProcessingException, IOException{
        logger.debug("Getting API's from Marathon configuration and posting to DB. environmentId = " + environmentId);

        List<ComponentVO> oldAPIComponentVOs = DBQueryUtil.getAllAPIComponentDetails(environmentId, SurveillerConstants.MESOS_PLATFORM, regionId);
        List<ComponentVO> newAPIComponentVOs = new ArrayList<ComponentVO>();
        List<HealthCheckVO> statusChangedList = new ArrayList<HealthCheckVO>();
        ObjectMapper mapper = new ObjectMapper();
        JsonNode rootNode = mapper.readTree(json);
        ArrayNode appsNode = (ArrayNode) rootNode.get("apps");
        Iterator<JsonNode> appsIterator = appsNode.getElements();
        while (appsIterator.hasNext()) {
            JsonNode appsInNode = appsIterator.next();
            JsonNode id = appsInNode.get("id");
            String idStr = id.getValueAsText();
            String idSubStr = idStr.substring(id.getValueAsText().indexOf('/') + 1);
            if(idSubStr.indexOf('/')  < 0){
                logger.info("Unable to process App Id : " + idStr);
                continue;
            }
            String appName = idSubStr.substring(0, idSubStr.indexOf('/'));
            String apiName = idSubStr.substring(idSubStr.indexOf('/')+1);
            
            Integer tasksRunning = appsInNode.get(SurveillerConstants.NUM_OF_TASKS_RUNNING) == null ? null : appsInNode.get(SurveillerConstants.NUM_OF_TASKS_RUNNING).getIntValue();
            Integer tasksStaged = appsInNode.get(SurveillerConstants.NUM_OF_TASKS_RUNNING) == null ? null : appsInNode.get(SurveillerConstants.NUM_OF_TASKS_STAGED).getIntValue();
            
            if(tasksRunning != null && tasksRunning.intValue() == 0 && tasksStaged != null && tasksStaged.intValue() == 0){
            	continue;
            }
            
            JsonNode lblNode = appsInNode.get("labels");
            int apiId = 0;
            if(lblNode != null && lblNode.get("HEALTHCHECK") != null){
                String healthCheckUrl = lblNode.get("HEALTHCHECK").getTextValue();
                if(healthCheckUrl != null && !healthCheckUrl.trim().equals("")){
                    apiId = DBQueryUtil.checkAndInsertComponent(appName, apiName, ComponentType.APP, healthCheckUrl, environmentId,
                    		regionId, HealthCheckType.URL_200_CHECK, SurveillerConstants.MESOS_PLATFORM);
                }
            }
            // Case in which there is no health check URL configure inside label
            // Mark as Warning if there is atleast one unhealthy task
            // Mark as Down if all tasks are unhealthy
            if(apiId == 0){
            	Integer tasksUnhealthy = appsInNode.get("tasksUnhealthy") == null ? null : appsInNode.get("tasksUnhealthy").getIntValue();
                if(tasksRunning != null && tasksUnhealthy != null && tasksRunning.intValue() != 0){
                    Status status = Status.UP;
                    if(tasksRunning.equals(tasksUnhealthy) ){
                        status = Status.DOWN;
                    }else if(tasksUnhealthy.intValue() != 0){
                        status = Status.WARNING;
                    }
                    apiId = DBQueryUtil.checkAndInsertComponent(appName, apiName, ComponentType.APP, null, environmentId,
                    		regionId, HealthCheckType.DUMMY, SurveillerConstants.MESOS_PLATFORM);
                    String marathonMessage = "Marathon Status - Running Tasks = " + tasksRunning + "; UnHealthy Task = " + tasksUnhealthy;
                    boolean isStatusChanged = DBQueryUtil.checkAndUpdateMessage(apiId,environmentId,regionId, HealthCheckType.DUMMY,status,marathonMessage);
                    if(isStatusChanged){
                    	statusChangedList.add(createVoWithComponentDetails(apiId,environmentId,regionId,status,marathonMessage));
                    }
                    DBQueryUtil.updateMarathonHealthCheckStatus(apiId,environmentId,regionId, HealthCheckType.DUMMY,status, isStatusChanged);
                }
            }
            if(apiId != 0){
                newAPIComponentVOs.add(DBQueryUtil.getComponent(apiId));
            }
        }
        compareAPILists(oldAPIComponentVOs, newAPIComponentVOs, environmentId, regionId);
        if(statusChangedList.size() > 0){
        	CommonsUtil.sendMailForChangedStatus(statusChangedList);
        }
    }
    
    /**
     * Create VO With Component Details to send mail for status change.
     * 
     * @param apiId ID of API
     * @param environmentId ID for environment
     * @param regionId ID for region
     * @param status Status of API
     * @param marathonMessage Marathon message
     */
    private static HealthCheckVO createVoWithComponentDetails(int apiId, int environmentId, Long regionId, Status status,
			String marathonMessage) {
    	HealthCheckVO returnVal = new HealthCheckVO();
    	returnVal.setHealthCheckComponentId((long)apiId);
    	returnVal.setEnvironmentId(environmentId);
    	returnVal.setHealthCheckRegionId(regionId);
    	returnVal.setStatus(new StatusVO(status, marathonMessage));
    	returnVal.setFailureStatusMessage(marathonMessage);
    	return returnVal;
	}
    
	/**
     * Function to compare old list of ComponentVO and new list ComponentVO and update the delInd
     * @param oldAPIComponentVOs
     * @param newAPIComponentVOs
     * @throws SQLException
     */
    public static void compareAPILists(List<ComponentVO> oldAPIComponentVOs, List<ComponentVO> newAPIComponentVOs, int enviromentId, long regionId) throws SQLException{
        logger.debug("Comparing old DB API list and new API list from marathon config");
        for(ComponentVO cVo : oldAPIComponentVOs){
            int compId = cVo.getComponentId();
            boolean isfound = false;
            for(ComponentVO newCVo : newAPIComponentVOs){
                if(newCVo.getComponentId() == compId){
                    isfound = true;
                    break;
                }
            }
            //Update delInd of component and healthCheck. Note :- cVo.getDelInd() is the DEL_IND of healthCheck table - refer DB_QUERY_GET_COMPONENT_DETAILS
            if(!isfound && cVo.getDelInd() == 0){
                DBQueryUtil.updateDelIndForHealthCheck(compId, 1, enviromentId, regionId);
            } else if(isfound && cVo.getDelInd() == 1){
                DBQueryUtil.updateDelIndForHealthCheck(compId, 0, enviromentId, regionId);
            }
        }
    }
    
}