package org.exoplatform.portal.rest;

import java.util.Locale;
import java.util.ResourceBundle;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

import org.exoplatform.commons.utils.PropertyManager;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.services.resources.ResourceBundleService;

public class UserFieldValidator {

  private static final int             DEFAULT_MIN_LENGTH = 3;

  private static final int             DEFAULT_MAX_LENGTH = 255;

  private static final int             MAX_FIELD_LENGTH   = 255;

  private static final String          KEY_PREFIX         = "gatein.validators.";

  private static final String          ALLOWED_SYMBOLS    = "'_', '.'";

  private static ResourceBundleService resourceBundleService;

  private int                          minLength          = DEFAULT_MIN_LENGTH;

  private int                          maxLength          = DEFAULT_MAX_LENGTH;

  private boolean                      usernameValidation = false;

  private boolean                      personalNameValidation = false;

  private String                       field              = null;

  private String                       pattern            = null;

  private String                       formatMessage      = null;

  public UserFieldValidator(String field, boolean usernameValidation, boolean personalNameValidation) {
    this(field, usernameValidation, personalNameValidation, DEFAULT_MIN_LENGTH, DEFAULT_MAX_LENGTH);
  }

  public UserFieldValidator(String field, boolean usernameValidation, boolean personalNameValidation, int defaultMin, int defaultMax) {
    this.field = field;
    this.usernameValidation = usernameValidation;
    this.personalNameValidation = personalNameValidation;

    String prefixedKey = KEY_PREFIX + field;

    pattern = PropertyManager.getProperty(prefixedKey + ".regexp");
    formatMessage = PropertyManager.getProperty(prefixedKey + ".format.message");

    String minProperty = PropertyManager.getProperty(prefixedKey + ".length.min");
    String maxProperty = PropertyManager.getProperty(prefixedKey + ".length.max");
    if (StringUtils.isNotBlank(minProperty)) {
      minLength = Integer.valueOf(minProperty);
    } else {
      minLength = defaultMin;
    }
    if (StringUtils.isNotBlank(maxProperty)) {
      maxLength = Integer.valueOf(maxProperty);
    } else {
      maxLength = defaultMax;
    }
    if (maxLength > MAX_FIELD_LENGTH) {
      maxLength = MAX_FIELD_LENGTH;
    }
  }

  public String validate(Locale locale, String value) {
    if (StringUtils.isBlank(value)) {
      String label = getFieldLabel(locale);
      return getLabel(locale, "EmptyFieldValidator.msg.empty-input", label);
    }
    char[] buff = value.toCharArray();
    if (buff.length < minLength || buff.length > maxLength) {
      String label = getFieldLabel(locale);
      return getLabel(locale, "StringLengthValidator.msg.length-invalid", label, minLength, maxLength);
    }

    if (StringUtils.isNotBlank(pattern) && !Pattern.matches(pattern, value)) {
      if (StringUtils.isNotBlank(formatMessage)) {
        return formatMessage;
      } else {
        String label = getFieldLabel(locale);
        return getLabel(locale, "ExpressionValidator.msg.value-invalid", label, pattern);
      }
    } else if (personalNameValidation) {
      for (int i = 0; i < buff.length - 1; i++) {
        char c = buff[i];
        if (Character.isLetter(c) || Character.isSpaceChar(c) || c == '\'' || c == '-') {
          continue;
        } else {
          String label = getFieldLabel(locale);
          return getLabel(locale, "PersonalNameValidator.msg.Invalid-char", label);
        }
      }
    } else if (usernameValidation) {
      if (!Character.isLowerCase(buff[0])) {
        String label = getFieldLabel(locale);
        return getLabel(locale, "FirstCharacterUsernameValidator.msg", label);
      }

      char c = buff[buff.length - 1];
      if (!isLowerCaseLetterOrDigit(c)) {
        String label = getFieldLabel(locale);
        return getLabel(locale, "LastCharacterUsernameValidator.msg", label, String.valueOf(c));
      }

      for (int i = 1; i < buff.length - 1; i++) {
        c = buff[i];
        if (isLowerCaseLetterOrDigit(c)) {
          continue;
        }
        if (isSymbol(c)) {
          char next = buff[i + 1];
          if (isSymbol(next)) {
            String label = getFieldLabel(locale);
            return getLabel(locale, "ConsecutiveSymbolValidator.msg", label, ALLOWED_SYMBOLS);
          } else if (!Character.isLetterOrDigit(next)) {
            String label = getFieldLabel(locale);
            return getLabel(locale, "UsernameValidator.msg.Invalid-char", label);
          }
        } else {
          String label = getFieldLabel(locale);
          return getLabel(locale, "UsernameValidator.msg.Invalid-char", label);
        }
      }
    }

    return null;
  }

  public String getField() {
    return field;
  }

  private String getFieldLabel(Locale locale) {
    String label = getLabel(locale, "UIRegisterForm.label." + field);
    return label == null ? null : label.replace(" :", "").replace(":", "");
  }

  private static boolean isLowerCaseLetterOrDigit(char character) {
    return Character.isDigit(character) || (character >= 'a' && character <= 'z');
  }

  private static boolean isSymbol(char c) {
    return c == '_' || c == '.';
  }

  private static String getLabel(Locale locale, String key, Object... values) {
    ResourceBundle resourceBundle = getResourceBundle(locale);
    if (resourceBundle == null || !resourceBundle.containsKey(key)) {
      resourceBundle = getResourceBundle(Locale.ENGLISH);
    }
    if (resourceBundle != null && resourceBundle.containsKey(key)) {
      String label = resourceBundle.getString(key);
      if (values != null) {
        for (int i = 0; i < values.length; i++) {
          String messageArg = String.valueOf(values[i]);
          if (messageArg != null) {
            label = label.replace("{" + i + "}", messageArg);
          }
        }
      }
      return label;
    }
    return key;
  }

  private static ResourceBundle getResourceBundle(Locale locale) {
    return getResourceBundleService().getResourceBundle(getResourceBundleService().getSharedResourceBundleNames(), locale);
  }

  private static ResourceBundleService getResourceBundleService() {
    if (resourceBundleService == null) {
      resourceBundleService = ExoContainerContext.getService(ResourceBundleService.class);
    }
    return resourceBundleService;
  }
}
