package hirondelle.predict.pub.register;

import static hirondelle.web4j.util.Consts.FAILS;
import hirondelle.predict.util.Captcha;
import hirondelle.web4j.model.Check;
import hirondelle.web4j.model.Id;
import hirondelle.web4j.model.ModelCtorException;
import hirondelle.web4j.model.ModelUtil;
import hirondelle.web4j.security.SafeText;

/** Model Object for a registering user, creating a new account. */
public final class Register {

  /**
  * Full constructor. All params are required. 
  *  
  *  <P>The CAPTCHA fields must be present, and must carry a valid response.
  * @param aLoginName Name used when logging in (required)
  * @param aScreenName User's name as typically displayed on the screen (required)
  * @param aPassword Login password, 6..50 chars (required)
  * @param aPasswordConfirm must be same as password (required)
  * @param aEmail Valid email address (required)
  * @param aCaptchaChallenge (required)
  * @param aCaptchaResponse (required)
  * @param aIpAddress the client IP address (required)
  */
  public Register(
   SafeText aLoginName, 
   SafeText aScreenName, 
   SafeText aPassword, 
   SafeText aPasswordConfirm, 
   SafeText aEmail, 
   SafeText aCaptchaChallenge,
   SafeText aCaptchaResponse,
   Id aIpAddress
  ) throws ModelCtorException {
    fLoginName = aLoginName;
    fScreenName = aScreenName;
    fPassword = aPassword;
    fPasswordConfirm = aPasswordConfirm;
    fEmail = aEmail;
    fCaptchaChallenge = aCaptchaChallenge;
    fCaptchaResponse = aCaptchaResponse;
    fIpAddress = aIpAddress;
    validateState();
  }
  
  public SafeText getLoginName() { return fLoginName; }  
  public SafeText getScreenName() { return fScreenName; }  
  public SafeText getPassword() { return fPassword; }  
  public SafeText getPasswordConfirm() { return fPasswordConfirm; }  
  public SafeText getEmail() { return fEmail; }  

  /** Intended for debugging only. Passwords are not emitted. */
  @Override public String toString(){
    StringBuilder builder = new StringBuilder();
    builder.append(Register.class);
    builder.append(" Login Name: " + fLoginName +  " Email: " + fEmail);
    return builder.toString();
  }
 
  @Override public boolean equals(Object aThat){
    Boolean result = ModelUtil.quickEquals(this, aThat);
    if ( result == null ) {
      Register that = (Register)aThat;
      result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
    }
    return result;
  }
  
  @Override public int hashCode(){
    if ( fHashCode == 0 ) {
      fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
    }
    return fHashCode;
  }

  // PRIVATE //
  private final SafeText fLoginName;
  private final SafeText fScreenName;
  private final SafeText fPassword;
  private final SafeText fPasswordConfirm;
  private final SafeText fEmail;
  private final SafeText fCaptchaChallenge;
  private final SafeText fCaptchaResponse;
  private final Id fIpAddress;
  private int fHashCode;

  private void validateState() throws ModelCtorException {
    ModelCtorException ex = new ModelCtorException();
    
    if ( FAILS == Check.required(fLoginName, Check.range(6, 50)) ) {
      ex.add("Login Name is required, must be at least 6 characters.");
    }
    if ( FAILS == Check.required(fScreenName, Check.range(6, 50)) ) {
      ex.add("Screen Name is required, minimum 6 characters.");
    }
    if ( FAILS == Check.required(fPassword, Check.range(6, 50)) ) {
      ex.add("Password is required, minimum 6 characters.");
    }
    if ( FAILS == Check.required(fPasswordConfirm, Check.range(6, 50)) ) {
      ex.add("Password confirmation is required, must match the password supplied above.");
    }
    if( bothPasswordsPresent() && passwordsDontMatch() ){
      ex.add("Password confirmation doesn't match the original password.");      
    }
    if ( FAILS == Check.required(fEmail, Check.email()) ) {
      ex.add("Valid email address is required (in case you forget your password).");
    }
    if( FAILS == isCaptchaPresent() ){
      ex.add("Please type in the fuzzy characters (CAPTCHA)");
    }
    else  if(  FAILS == isCaptchaValid() ){
      ex.add("CAPTCHA invalid. Please type in the fuzzy characters (CAPTCHA) again.");
    }

    if ( ! ex.isEmpty() ) throw ex;
  }
  
  private Object[] getSignificantFields(){
    return new Object[] {fLoginName, fScreenName, fPassword, fPasswordConfirm, fEmail};
  }
  
  private boolean isCaptchaPresent(){
    return Check.required(fCaptchaChallenge) && Check.required(fCaptchaResponse);
  }
  
  private boolean isCaptchaValid(){
    return Captcha.isCaptchaValid(
      fIpAddress.getRawString(), fCaptchaChallenge.getRawString(), fCaptchaResponse.getRawString()
    );
  }
  
  private boolean bothPasswordsPresent(){
    return Check.required(fPassword) && Check.required(fPasswordConfirm);    
  }
  
  private boolean passwordsDontMatch(){
    return ! fPassword.equals(fPasswordConfirm);
  }
}