package com.capgemini.framework.common;

import com.capgemini.framework.common.security.AesEncoded;
import com.capgemini.framework.teamcenter.exceptions.KurtException;
import com.capgemini.reports.ReportEnum;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.TreeMap;

/**
 * Static class managing all properties from reporting.properties file.
 **/
@EqualsAndHashCode(callSuper = true)
@Slf4j
public final class SettingsHelper extends AbstractPropertiesManager {


  /**
   * Domain name (e.g. snecma.local).
   */
  private static final String KEY_DOMAIN_NAME = "authent.domain";
  /**
   * Prefix that should be appended at the beginning of all properties.
   **/
  private static final String PROP_PREFIX = "com.capgemini.report.";
  // Global modes
  private static final String KEY_CLEANUP = "report.cleanup";
  private static final String KEY_DEV_MODE = "devmode";
  private static final String KEY_VAL_MODE = "valmode";
  // Memory monitor
  private static final String KEY_MEM_MAX_MONITOR_TRACE_COUNT = "MngMemMonitor.maxTraceCount";
  private static final String KEY_MEM_FREQUENCY = "MngMemMonitor.frequency";
  // Email
  private static final String KEY_DEFAULT_DESTINATOR_EMAIL = "common.MailHelper.defaultDestinatorMail";
  private static final String KEY_DEFAULT_MAIL_USER = "common.MailHelper.defaultExpeditorMail";
  private static final String KEY_DEFAULT_MAIL_PASS = "common.MailHelper.defaultExpeditorPassword";
  private static final String KEY_DEFAULT_SMTP_HOST = "common.MailHelper.defaultSmtpHost";
  private static final String KEY_DEFAULT_SMTP_PORT = "common.MailHelper.defaultSmtpPort";
  // File retrieval
  private static final String KEY_DOWNLOAD_URL = "download.URL";
  // JSP Presentation
  private static final String KEY_JSP_PAGING = "jsp.defaultPaging";
  // Purge
  private static final String KEY_PURGE_DELAY_REPORTS = "nb.days";
  private static final String KEY_PURGE_DELAY_SCHEDULING = "nb.days.scheduling";
  // Launch times
  private static final String KEY_PURGE_HOUR = "common.launch.purge.hour";
  private static final String KEY_DELAYED_LAUNCH_HOUR = "common.launch.delayed.hour";
  private static final String KEY_DEFAULT_LAUNCH_TYPE = "common.launch.launchType.default";
  // Scheduling save location
  private static final String KEY_OUTPUT_FOLDER_REPORT = "location.default.output";
  private static final String KEY_OUTPUT_FOLDER_SCHEDULING = "location.scheduling.output";
  private static final String KEY_OUTPUT_FOLDER_SCHEDULING_HISTORY = "location.scheduling.history.output";
  // Functional values
  private static final String KEY_PRODUCT_LIST = "product.list";
  private static final String KEY_DEFAULT_REVISION_RULE = "common.bom.revisionRule.default";
  private static final String KEY_PREFIX_REPORT_TITLE = "title.";
  private static final String KEY_TECH_LOGIN = "authent.tech.login";
  private static final String KEY_TECH_PASSWORD = "authent.tech.password";
  /**
   * Provider URL+port. Ex: "LDAP://snecmadc.snecma.local:389/".
   */
  private static final String KEY_PROVIDER_URL = "authent.provider.url";
  private static final String KEY_PROVIDER_PORT = "authent.provider.port";
  /**
   * Organisational unit for user OU. e.g. 'OU=TechnicalAccounts'.
   */
  private static final String KEY_MAIL_ATTRIBUTE = "authent.user.attributes.email";
  private static final String KEY_LOGIN_ATTRIBUTE = "authent.user.attributes.login";
  private static final String KEY_FIRST_NAME_ATTRIBUTE = "authent.user.attributes.firstName";
  private static final String KEY_LAST_NAME_ATTRIBUTE = "authent.user.attributes.lastname";
  // ACTIVE DIRECTORY : Admin data
  private static final String KEY_ADMIN_GROUP = "authent.admin.group";
  private static final String KEY_ADMIN_ORGANISATIONAL_UNIT = "authent.admin.organisationalunit";

  private static final String KEY_OPTIMIZED_FOLDER_R23 = "optimized.folder.R23";
  private static final String KEY_OPTIMIZED_FOLDER_R24 = "optimized.folder.R24";
  /**
   * Singleton instance.
   **/
  private static AbstractPropertiesManager instance;

  /**
   * Constructor : load the properties from the property file.
   **/
  private SettingsHelper() {
    super("/reporting.properties");
  }

  /**
   * Returns the current dev mode status.
   *
   * @return true is the dev mode is active, false otherwise
   */
  public static boolean isDevMode() {
    return Boolean.parseBoolean(System.getProperty(KEY_DEV_MODE));
  }

  /**
   * Returns the current val mode status.
   *
   * @return true is the val mode is active, false otherwise
   */
  public static boolean isValMode() {
    return Boolean.parseBoolean(System.getProperty(KEY_VAL_MODE));
  }

  /**
   * Returns the current cleanup mode. If mode is unspecified, defaults to false.
   *
   * @return true iff the cleanup is activated
   */
  public static boolean isCleanup() {
    return getBooleanWithDefaultValue(KEY_CLEANUP, false);
  }

  /**
   * The max number of traces to keep in DB for {@link MemoryHelper}.
   *
   * @return an int
   */
  public static int getMemoryMaxCount() {
    return getInteger(KEY_MEM_MAX_MONITOR_TRACE_COUNT);
  }

  /**
   * The memory update frequency for {@link MemoryHelper}.
   *
   * @return an int
   */
  public static int getMemoryFrequency() {
    return getInteger(KEY_MEM_FREQUENCY);
  }

  /**
   * Maximum number of JSP result per page, when said page contains a table.
   *
   * @return number of rows
   */
  public static int getJspPaging() {
    return Integer.parseInt(System.getProperty(KEY_JSP_PAGING));
  }

  /**
   * Returns the number of days after which usual reports are considered stale and will be deleted.
   *
   * @return a number of days (positive)
   */
  public static int getPurgeDelayReports() {
    return Integer.parseInt(System.getProperty(KEY_PURGE_DELAY_REPORTS));
  }

  /**
   * Returns the number of days after which scheduled reports kept in history are considered stale and will be deleted.
   *
   * @return a number of days (positive)
   */
  public static int getPurgeDelayScheduling() {
    return Integer.parseInt(System.getProperty(KEY_PURGE_DELAY_SCHEDULING));
  }

  /**
   * REturns the full title of the report.
   *
   * @param reportEnum the report whose title is requested
   * @return the title
   */
  public static String getTitle(ReportEnum reportEnum) {
    return getInstance().getStringValue(KEY_PREFIX_REPORT_TITLE + reportEnum.toString());
  }

  /**
   * The Default launch type (immediate).
   *
   * @return the folder
   */
  public static String getDefaultLaunchType() {
    return getInstance().getStringValue(KEY_DEFAULT_LAUNCH_TYPE);
  }

  /**
   * The folder location where reports are kept.
   *
   * @return the folder
   */
  public static String getReportLocation() {
    return System.getProperty(KEY_OUTPUT_FOLDER_REPORT);
  }

  /**
   * The folder location where the latest report of each scheduling is kept.
   *
   * @return the folder
   */
  public static String getSchedulingLocation() {
    return System.getProperty(KEY_OUTPUT_FOLDER_SCHEDULING);
  }

  /**
   * The folder location where scheduling history is kept.
   *
   * @return the folder
   */
  public static String getSchedulingHistoryLocation() {
    return System.getProperty(KEY_OUTPUT_FOLDER_SCHEDULING_HISTORY);
  }

  /**
   * The time a which the purge begins every day. format "HH:mm".
   *
   * @return the String time
   */
  public static String getPurgeHour() {
    return System.getProperty(KEY_PURGE_HOUR);
  }

  // FTP CREDENTIALS FOR VALIDATION, NOT PRODUCTION

  /**
   * The time a which the purge begins every day. format "HH:mm".
   *
   * @return the String time
   */
  public static String getDelayedLaunchHour() {
    return System.getProperty(KEY_DELAYED_LAUNCH_HOUR);
  }

  /**
   * The default email recipients when no email address is found.
   *
   * @return a String {@link List}
   */
  public static List<String> getDefaultDestinatorEmail() {
    return getStringList(KEY_DEFAULT_DESTINATOR_EMAIL);
  }


  // ACTIVE DIRECTORY CREDENTIALS

  // ACTIVE DIRECTORY : Technical account data

  /**
   * The default email recipients when no email address is found.
   *
   * @return a String with all recipients, com-separated
   */
  public static String getDefaultDestinatorEmailFull() {
    return System.getProperty(KEY_DEFAULT_DESTINATOR_EMAIL);
  }

  /**
   * Obtain a report's priority.
   *
   * @param report the report to investigate
   * @return its priority : 1(high) - 3(low)
   */
  public static int getPriority(ReportEnum report) {
    String completeReportKey = "priority." + report.toString();
    return getInteger(completeReportKey);
  }

  /**
   * Reads the properties file, to obtain the list of product types.<br>
   * The list must be comma-separated, without spaces, like 120,238,410,700,80C,80E,850,8LM,900,970
   *
   * @return the individual product types that are authorized, in a {@link List}. They are non-null, not empty.
   */
  public static List<String> getProductTypes() {
    return getStringList(KEY_PRODUCT_LIST);
  }

  /**
   * Obtain the URL at which documents may be retrieved. The URL must still be appended with the document name.
   *
   * @return the root download URL.
   */
  public static String getDownloadUrlRoot() {
    return getString(KEY_DOWNLOAD_URL);
  }

  /**
   * Obtain the URL at which documents may be retrieved. The URL must still be appended with the document name.
   *
   * @return the root download URL.
   */
  public static String getDefaultRevisionRule() {
    return getInstance().getStringValue(KEY_DEFAULT_REVISION_RULE);
  }

  // ACTIVE DIRECTORY : User informations

  /**
   * Obtain the username of the expeditor for mails, when none is provided.
   *
   * @return the username.
   */
  public static String getDefaultExpeditorMailUsername() {
    return getString(KEY_DEFAULT_MAIL_USER);
  }

  /**
   * Obtain the password of the expeditor for mails, when none is provided.
   *
   * @return the password.
   */
  public static String getDefaultExpeditorMailPassword() {
    return getString(KEY_DEFAULT_MAIL_PASS);
  }

  /**
   * Obtain the SMTP host for emailing.
   *
   * @return the host.
   */
  public static String getDefaultSmtpHost() {
    return getString(KEY_DEFAULT_SMTP_HOST);
  }

  /**
   * Obtain the SMTP port for emailing.
   *
   * @return the port.
   */
  public static String getDefaultSmtpPort() {
    return getString(KEY_DEFAULT_SMTP_PORT);
  }

  public static String getOptimizedFolderR23() {
    return getString(KEY_OPTIMIZED_FOLDER_R23);
  }

  public static String getOptimizedFolderR24() {
    return getString(KEY_OPTIMIZED_FOLDER_R24);
  }


  /**
   * Obtain the credentials to access the ActiveDirectory for authentication.
   *
   * @return the credentials
   */
  public static ActiveDirectoryCredentials getActiveDirectoryCredentials() {
    // Domain name. Ex: "snecma.local".
    String domain = System.getProperty(KEY_DOMAIN_NAME);
    String login = System.getProperty(KEY_TECH_LOGIN);
    String pass;
    try {
      pass = new AesEncoded(System.getProperty(KEY_TECH_PASSWORD)).decode();
    }
    catch (KurtException e) {
      log.error("Error decoding " + KEY_TECH_PASSWORD, e);
      pass = "";
    }
    // Provider URL. Ex: "LDAP://snecmadc.snecma.local".
    String providerURL = System.getProperty(KEY_PROVIDER_URL);
    // Provider port. Ex: "389".
    String providerPort = System.getProperty(KEY_PROVIDER_PORT);
    return new ActiveDirectoryCredentials(domain, login, pass, providerURL, providerPort);
  }

  /**
   * Obtain the ActiveDirectory Attributes names to request some data.
   *
   * @return the attibutes names
   */
  public static ActiveDirectoryAttributes getActiveDirectoryAttributes() {
    String mail = System.getProperty(KEY_MAIL_ATTRIBUTE);
    String login = System.getProperty(KEY_LOGIN_ATTRIBUTE);
    String firstName = System.getProperty(KEY_FIRST_NAME_ATTRIBUTE);
    String lastName = System.getProperty(KEY_LAST_NAME_ATTRIBUTE);
    return new ActiveDirectoryAttributes(mail, login, firstName, lastName);
  }

  /**
   * Obtain the ActiveDirectory Attributes names to request some data.
   *
   * @return the attibutes names
   */
  public static ActiveDirectoryAdministration getActiveDirectoryAdministration() {
    String group = System.getProperty(KEY_ADMIN_GROUP);
    String organisationalUnit = System.getProperty(KEY_ADMIN_ORGANISATIONAL_UNIT);
    return new ActiveDirectoryAdministration(group, organisationalUnit);
  }

  /**
   * Returns a {@link List} of comma-separated values. The values are trimmed, and empty values are omitted.
   *
   * @param aKey Searched setting key
   * @return a List of non-empty values
   */
  private static List<String> getStringList(String aKey) {
    String rawList = System.getProperty(aKey);
    if (rawList == null) {
      log.warn("Could not find the property list " + aKey);
      return new ArrayList<>();
    }
    // Trim and split
    String[] split = rawList.trim().split(",");
    // Browse values
    List<String> list = new ArrayList<>();
    for (String elem : split) {
      if (elem != null) { // Remove null
        String trimmedElem = elem.trim(); // Trim
        if (!trimmedElem.isEmpty()) { // Remove empty values
          list.add(elem);
        }
      }
    }
    return list;
  }

  /**
   * Get Integer value from settings.
   *
   * @param aKey Searched setting key
   * @return integer value matching with searched setting key
   */
  public static int getInteger(final String aKey) {
    return getInstance().getIntegerValue(aKey);
  }

  /**
   * Get a string value from settings.
   *
   * @param aKey Searched setting key
   * @return String value matching with searched setting key
   */
  public static String getString(final String aKey) {
    return System.getProperty(aKey);
  }

  /**
   * Get a property from the reference parameter and set a default value if the reference is not found.
   *
   * @param aRef          Setting key
   * @param aDefaultValue Default returned value when setting is not defined
   * @return string value matching with searched setting key or default value when key has not been found
   */
  public static String getPropertyWithDefaultValue(final String aRef, final String aDefaultValue) {
    return getInstance().getPropertyWithDefaultValueImpl(aRef, aDefaultValue);
  }

  /**
   * Get a boolean from the reference parameter and set a default value if the reference is not found.
   *
   * @param aRef          Setting key
   * @param aDefaultValue Default returned value when setting is not defined
   * @return boolean value matching with searched setting key or default value when key has not been found
   */
  private static boolean getBooleanWithDefaultValue(final String aRef, final boolean aDefaultValue) {
    return getInstance().getBooleanWithDefaultValueImpl(aRef, aDefaultValue);
  }

  /**
   * @return the instance.
   **/
  private static synchronized AbstractPropertiesManager getInstance() {
    if (instance == null) {
      instance = new SettingsHelper();
    }
    return instance;
  }

  /**
   * Getter for the properties common prefixes.
   *
   * @return PROP_PREFIX value
   **/
  @Override
  protected String getPropPrefix() {
    return PROP_PREFIX;
  }

  /**
   * Display the string content of a property file.
   *
   * @return String representation of sorted properties
   **/
  @Override
  public String toString() {

    StringBuilder thePropValues = new StringBuilder();

    // Browse all properties for sorting them in memory
    Map<String, String> theSortedMap = new TreeMap<>();
    Properties propertyFile2 = getPropertyFile();
    for (Entry<Object, Object> currentEntry : propertyFile2.entrySet()) {
      theSortedMap.put(currentEntry.getKey().toString(), currentEntry.getValue().toString());
    }

    // Browse all sorted properties
    for (Entry<String, String> currentEntry : theSortedMap.entrySet()) {
      thePropValues.append(currentEntry.getKey()).append('=').append(currentEntry.getValue()).append(StringHelper.LINE_FEED);
    }

    return thePropValues.toString();
  }

  /**
   * POJO holding ActiveDirectory credential data.
   */
  @Getter
  public static final class ActiveDirectoryCredentials {
    /**
     * Domain name (e.g. snecma.local).
     */
    private final String domainName;
    private final String techLogin;
    private final String techPassword;
    private final String providerURL;
    private final String providerPort;

    public ActiveDirectoryCredentials(String domainName, String techLogin, String techPassword, String providerURL, String providerPort) {
      this.domainName = domainName;
      this.techLogin = techLogin;
      this.techPassword = techPassword;
      this.providerURL = providerURL;
      this.providerPort = providerPort;
    }
  }

  /**
   * POJO holding ActiveDirectory credential data.
   */
  public static final class ActiveDirectoryAttributes {
    private final String email;
    private final String login;
    private final String firstName;
    private final String lastName;

    public ActiveDirectoryAttributes(String email, String login, String firstName, String lastName) {
      this.email = email;
      this.login = login;
      this.firstName = firstName;
      this.lastName = lastName;
    }

    public String getEmail() {
      return email;
    }

    public String getLogin() {
      return login;
    }

    public String getFirstName() {
      return firstName;
    }

    public String getLastName() {
      return lastName;
    }

  }

  /**
   * POJO holding ActiveDirectory credential data.
   */
  @Getter
  public static final class ActiveDirectoryAdministration {
    private final String adminGroup;
    private final String adminOU;

    public ActiveDirectoryAdministration(String adminGroup, String adminOU) {
      this.adminGroup = adminGroup;
      this.adminOU = adminOU;
    }
  }

}
