package org.exoplatform.social.service.malwareDetection.connector;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.ws.rs.core.MediaType;

import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import org.exoplatform.container.xml.InitParams;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;

public class TrendMicroMalwareDetectionConnector extends MalwareDetectionConnector {
  
  private static final Log LOGGER = ExoLogger.getExoLogger(TrendMicroMalwareDetectionConnector.class);
  private static final String TRENDMICRO_HOST_PROPERTY = "exo.malwareDetection.connector.trendMicro.api.host";
  private static final String TRENDMICRO_USERNAME_PROPERTY = "exo.malwareDetection.connector.trendMicro.api.userName";
  private static final String TRENDMICRO_PASSWORD_PROPERTY = "exo.malwareDetection.connector.trendMicro.api.password";
  private static final String TRENDMICRO_LIST_EVENTS_DELTA_PROPERTY = "exo.malwareDetection.connector.trendMicro.api.listEvents.delta";
  private static final String TRENDMICRO_LIST_EVENTS_DELTA_PROPERTY_DEFAULT = "900000";//In millisecondes, 15 minutes by default 
  private static final String MALWARE_DETECTION_JOB_PERIOD_PROPERTY = "exo.social.MalwareDetectionJob.period";
  private static final String MALWARE_DETECTION_JOB_PERIOD_PROPERTY_DEFAULT = "1800000";//In millisecondes, 30 minutes by default 
  private static final String TRENDMICRO_LOGIN_ENDPOINT = "/rest/authentication/login";
  private static final String TRENDMICRO_LOGIN_OPERATION = "trendmicro-login";
  private static final String TRENDMICRO_LIST_EVENTS_ENDPOINT = "/rest/events/antimalware";
  private static final String TRENDMICRO_LIST_EVENTS_OPERATION = "trendmicro-list-events";
  private static final String TRENDMICRO_SID_QUERY_PARAM = "?sID=";
  private static final String TRENDMICRO_EVENT_TIME_OP_QUERY_PARAM = "&eventTimeOp=";
  private static final String TRENDMICRO_EVENT_TIME_OP_GE = "ge";
  private static final String TRENDMICRO_EVENT_TIME_QUERY_PARAM = "&eventTime=";
  private static final String TRENDMICRO_ANTI_MALWARE_EVENT_LISTING = "antiMalwareEventListing";
  private static final String TRENDMICRO_EVENTS = "events";
  private static final String TRENDMICRO_INFECTED_FILE_PATH = "infectedFilePath";
  private static final String TRENDMICRO_INFECTED_FILE_PATH_SEPARATOR = "(";
  private static final String TRENDMICRO_LOGOUT_ENDPOINT = "/rest/authentication/logout";
  private static final String TRENDMICRO_LOGOUT_OPERATION = "trendmicro-logout";

  public TrendMicroMalwareDetectionConnector(InitParams initParams) {
    super(initParams);
  }
  
  @Override
  public List<String> getInfectedItemsPaths() {
    // Trendmicro login
    String sid = login();
    List<String> infectedItemsPaths = new ArrayList<String>();
    if (sid != null) {
      // Trendmicro list events
      String eventsListResponse = getEventsList(sid);
      long startTime = System.currentTimeMillis();
      try {
        JSONObject eventsListJson = new JSONObject(eventsListResponse);
        if (eventsListJson.has(TRENDMICRO_ANTI_MALWARE_EVENT_LISTING)) {
          JSONObject antiMalwareEventListingJson = eventsListJson.getJSONObject(TRENDMICRO_ANTI_MALWARE_EVENT_LISTING);
          if (antiMalwareEventListingJson.has(TRENDMICRO_EVENTS)) {
            JSONArray eventsJsonArray = antiMalwareEventListingJson.getJSONArray(TRENDMICRO_EVENTS);
            for (int i = 0; i < eventsJsonArray.length(); i++) {
              JSONObject eventJson = eventsJsonArray.getJSONObject(i);
              if (eventJson.has(TRENDMICRO_INFECTED_FILE_PATH)) {
                String infectedFilePath = StringUtils.substringBeforeLast(eventJson.getString(TRENDMICRO_INFECTED_FILE_PATH), TRENDMICRO_INFECTED_FILE_PATH_SEPARATOR);
                if(!infectedItemsPaths.contains(infectedFilePath)) {
                  infectedItemsPaths.add(infectedFilePath);
                }
              }
            }
          }
        }
        long endTime = System.currentTimeMillis();
        LOGGER.info("service={} operation={} status=ok duration_ms={}",
                    MALWARE_DETECTION_FEATURE,
                    GET_MALWARE_INFECTED_ITEMS_PATHS_OPERATION,
                    endTime - startTime);
      } catch (JSONException e) {
        long endTime = System.currentTimeMillis();
        LOGGER.error("service={} operation={} parameters=\"eventsList:{}\" status=ko duration_ms={} error_msg=\"Error when trying to get the infected items paths:{}\"",
                    MALWARE_DETECTION_FEATURE,
                    GET_MALWARE_INFECTED_ITEMS_PATHS_OPERATION,
                    eventsListResponse,
                    endTime - startTime,
                    e);
      }
      // Trendmicro logout
      logout(sid);
    }
    return infectedItemsPaths;
  }
  
  private String login() {
    String trendmicroHost = System.getProperty(TRENDMICRO_HOST_PROPERTY);
    String trendmicroUserName = System.getProperty(TRENDMICRO_USERNAME_PROPERTY);
    String trendmicroPassword = System.getProperty(TRENDMICRO_PASSWORD_PROPERTY);
    String trendmicroLoginEndpoint = TRENDMICRO_LOGIN_ENDPOINT;
    if (trendmicroHost != null && trendmicroUserName != null &&  trendmicroPassword != null) {
      long startTime = System.currentTimeMillis();
      try {
        URL trendmicroLoginUrl = new URL(trendmicroHost + trendmicroLoginEndpoint);
        HttpURLConnection trendmicroLoginUrlConnection = (HttpURLConnection) trendmicroLoginUrl.openConnection();
        // add request header
        trendmicroLoginUrlConnection.setDoOutput(true);
        trendmicroLoginUrlConnection.setRequestMethod("POST");
        trendmicroLoginUrlConnection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
        String dsCredentials = "{\"dsCredentials\": {\"userName\": \"" + trendmicroUserName + "\", \"password\": \"" +trendmicroPassword + "\"}}";
        OutputStream trendmicroLoginUrlConnectionOut = trendmicroLoginUrlConnection.getOutputStream();
        trendmicroLoginUrlConnectionOut.write(dsCredentials.getBytes(("UTF-8")));
        if (trendmicroLoginUrlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { //success
          try(BufferedReader br = new BufferedReader(new InputStreamReader(trendmicroLoginUrlConnection.getInputStream(), "utf-8"))) {
            StringBuilder response = new StringBuilder();
            String responseLine = null;
            while ((responseLine = br.readLine()) != null) {
              response.append(responseLine.trim());
            }
            long endTime = System.currentTimeMillis();
            LOGGER.info("remote_service={} operation={} parameters=\"host:{}\" \"userName:{}\" status=ok duration_ms={}",
                        MALWARE_DETECTION_FEATURE,
                        TRENDMICRO_LOGIN_OPERATION,
                        trendmicroHost,
                        trendmicroUserName,
                        endTime - startTime);
            return response.toString();
          }
        }
        long endTime = System.currentTimeMillis();
        LOGGER.error("remote_service={} operation={} parameters=\"host:{}\" \"userName:{}\" status=ko status_code={} duration_ms={} error_msg=\"Error when trying to login trendMicro\"",
                     MALWARE_DETECTION_FEATURE,
                     TRENDMICRO_LOGIN_OPERATION,
                     trendmicroHost,
                     trendmicroUserName,
                     trendmicroLoginUrlConnection.getResponseCode(),
                     endTime - startTime);
      } catch (Exception e) {
        long endTime = System.currentTimeMillis();
        LOGGER.error("remote_service={} operation={} parameters=\"host:{}\" \"userName:{}\" status=ko duration_ms={} error_msg=\"Error when trying to login trendMicro:{}\"",
                     MALWARE_DETECTION_FEATURE,
                     TRENDMICRO_LOGIN_OPERATION,
                     trendmicroHost,
                     trendmicroUserName,
                     endTime - startTime,
                     e);
      }
    }
    return null;
  }
  
  private String getEventsList(String sid) {
    String trendmicroHost = System.getProperty(TRENDMICRO_HOST_PROPERTY);
    String trendmicroListEventsEndpoint = TRENDMICRO_LIST_EVENTS_ENDPOINT;
    if (trendmicroHost != null) {
      long startTime = System.currentTimeMillis();
      try {
        StringBuilder trendmicroListEventsUrlSb = new StringBuilder(trendmicroHost);
        trendmicroListEventsUrlSb.append(trendmicroListEventsEndpoint);
        trendmicroListEventsUrlSb.append(TRENDMICRO_SID_QUERY_PARAM + sid);
        String malwareDetectionJobPeriod = System.getProperty(MALWARE_DETECTION_JOB_PERIOD_PROPERTY, MALWARE_DETECTION_JOB_PERIOD_PROPERTY_DEFAULT);
        String trendmicroListEventsDelta = System.getProperty(TRENDMICRO_LIST_EVENTS_DELTA_PROPERTY, TRENDMICRO_LIST_EVENTS_DELTA_PROPERTY_DEFAULT);
        trendmicroListEventsUrlSb.append(TRENDMICRO_EVENT_TIME_OP_QUERY_PARAM + TRENDMICRO_EVENT_TIME_OP_GE);
        long trendmicroListEventsPeriod = Long.parseLong(malwareDetectionJobPeriod) + Long.parseLong(trendmicroListEventsDelta);
        Date now = new Date();
        long trendmicroListEventsTime = now.getTime() - trendmicroListEventsPeriod;
        trendmicroListEventsUrlSb.append(TRENDMICRO_EVENT_TIME_QUERY_PARAM + trendmicroListEventsTime);
        URL trendmicroListEventsUrl = new URL(trendmicroListEventsUrlSb.toString());
        HttpURLConnection trendmicroListEventsUrlConnection = (HttpURLConnection) trendmicroListEventsUrl.openConnection();
        // add request header
        trendmicroListEventsUrlConnection.setRequestMethod("GET");
        if (trendmicroListEventsUrlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { //success
          try(BufferedReader br = new BufferedReader(new InputStreamReader(trendmicroListEventsUrlConnection.getInputStream(), "utf-8"))) {
             StringBuilder response = new StringBuilder();
             String responseLine = null;
             while ((responseLine = br.readLine()) != null) {
               response.append(responseLine.trim());
             }
             long endTime = System.currentTimeMillis();
             LOGGER.info("remote_service={} operation={} parameters=\"host:{}\" \"sID:{}\" status=ok duration_ms={}",
                         MALWARE_DETECTION_FEATURE,
                         TRENDMICRO_LIST_EVENTS_OPERATION,
                         trendmicroHost,
                         sid,
                         endTime - startTime);
             return response.toString();
           }
        }
        long endTime = System.currentTimeMillis();
        LOGGER.error("remote_service={} operation={} parameters=\"host:{}\" \"sID:{}\" status=ko status_code={} duration_ms={} error_msg=\"Error when trying to get trendmicro events list\"",
                     MALWARE_DETECTION_FEATURE,
                     TRENDMICRO_LIST_EVENTS_OPERATION,
                     trendmicroHost,
                     sid,
                     trendmicroListEventsUrlConnection.getResponseCode(),
                     endTime - startTime);
      } catch (Exception e) {
        long endTime = System.currentTimeMillis();
        LOGGER.error("remote_service={} operation={} parameters=\"host:{}\" \"sID:{}\" status=ko duration_ms={} error_msg=\"Error when trying to get trendmicro events list:{}\"",
                     MALWARE_DETECTION_FEATURE,
                     TRENDMICRO_LIST_EVENTS_OPERATION,
                     trendmicroHost,
                     sid,
                     endTime - startTime,
                     e);
      }
    }
    return null;
  }
  
  private void logout(String sid) {
    String trendmicroHost = System.getProperty(TRENDMICRO_HOST_PROPERTY);
    String trendmicroLogoutEndpoint = TRENDMICRO_LOGOUT_ENDPOINT;
    if (trendmicroHost != null) {
      long startTime = System.currentTimeMillis();
      try {
        StringBuilder trendmicroLogoutUrlSb = new StringBuilder(trendmicroHost);
        trendmicroLogoutUrlSb.append(trendmicroLogoutEndpoint);
        trendmicroLogoutUrlSb.append(TRENDMICRO_SID_QUERY_PARAM + sid);
        URL trendmicroLogoutUrl = new URL(trendmicroLogoutUrlSb.toString());
        HttpURLConnection trendmicroLogoutUrlConnection = (HttpURLConnection) trendmicroLogoutUrl.openConnection();
        // add request header
        trendmicroLogoutUrlConnection.setRequestMethod("DELETE");
        long endTime = System.currentTimeMillis();
        if (trendmicroLogoutUrlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { //success
          LOGGER.info("remote_service={} operation={} parameters=\"host:{}\" \"sID:{}\" status=ok duration_ms={}",
                      MALWARE_DETECTION_FEATURE,
                      TRENDMICRO_LOGOUT_OPERATION,
                      trendmicroHost,
                      sid,
                      endTime - startTime);
        }
        else {
          LOGGER.error("remote_service={} operation={} parameters=\"host:{}\" \"sID:{}\" status=ko status_code={} duration_ms={} error_msg=\"Error when trying to logout trendMicro\"",
                       MALWARE_DETECTION_FEATURE,
                       TRENDMICRO_LOGOUT_OPERATION,
                       trendmicroHost,
                       sid,
                       trendmicroLogoutUrlConnection.getResponseCode(),
                       endTime - startTime);
        }
      } catch (Exception e) {
        long endTime = System.currentTimeMillis();
        LOGGER.error("remote_service={} operation={} parameters=\"host:{}\" \"sID:{}\" status=ko duration_ms={} error_msg=\"Error when trying to logout trendMicro:{}\"",
                     MALWARE_DETECTION_FEATURE,
                     TRENDMICRO_LOGOUT_OPERATION,
                     trendmicroHost,
                     sid,
                     endTime - startTime,
                     e);
      }
    }
  }
}