/*
 * Created on 31. 7. 2017 16:49:36
 *
 */
package com.jh.radegast.security;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.jh.common.security.AuthService;
import com.jh.common.security.PasswordHash;
import com.jh.common.security.PasswordSecurity;
import com.jh.common.security.ResetPassword;
import com.jh.common.security.Signup;
import com.jh.common.security.controller.AuthError;
import com.jh.common.utils.Utils;

/**
 * The Class AppUserAuthService.
 *
 * @author Jan Hrabal
 */
@Service
public class AppUserAuthService implements AuthService {

	/** The sleep. */
	@Value("${auth.login.sleep:250}")
	private Integer sleep;
	
    /** The service. */
    @Autowired
    private AppUserService service;
    
    /**
     * @param username
     * @param password
     * @return
     * @throws BadCredentialsException
     * @see com.jh.common.security.AuthService#authenticate(java.lang.String, java.lang.String)
     */
    @Override
	public Authentication authenticate(String username, String password) throws BadCredentialsException {
    	Utils.sleep(sleep);
    	
		AppUser user = service.fetchUser(username);
        if (user == null) {
        	Utils.sleep(sleep);
            throw new BadCredentialsException("Bad username or password");
        }
        
        if (!PasswordSecurity.checkPassword(password, user.getPassword(), user.getPasswordSalt())) {
        	Utils.sleep(sleep);
        	throw new BadCredentialsException("Bad username or password");
        }
        
        //clear password & salt
        user.setPassword(null);
        user.setPasswordSalt(null);
        user.setDeleted(null);

        String role = user.getRole();
        if (!StringUtils.hasText(role)) {
            role = AppRole.ROLE_READ_ONLY;
        }
        
        return new UsernamePasswordAuthenticationToken(user, password, Collections.singleton(new SimpleGrantedAuthority(role)));
    }


	/**
	 * @param signup
	 * @return
	 * @see com.jh.common.security.AuthService#register(com.jh.common.security.Signup)
	 */
	@Override
	public List<AuthError> register(Signup signup) {
		Objects.requireNonNull(signup);

		List<AuthError> errors = new ArrayList<>();
		if (!StringUtils.hasText(signup.getUsername())) {
			errors.add(AuthError.usernameError("UsernameRequired"));
		} else if (!Utils.validateEmail(signup.getUsername())) {
            errors.add(AuthError.usernameError("BadUsername"));
		}
		if (!PasswordSecurity.validatePassword(signup.getPassword())) {
			errors.add(AuthError.passwordError("BadPassword"));
		}
		if (!errors.isEmpty()) {
		    return errors;
		}

		AppUser user = service.findByEmail(signup.getUsername());
		if (user == null) {
			user = new AppUser();
			user.setDeleted(Boolean.FALSE);
			user.setEmail(signup.getUsername());
			
			PasswordHash hash = PasswordSecurity.hashPassword(signup.getPassword());
			user.setPassword(hash.getHash());
			user.setPasswordSalt(hash.getSalt());
			user.setRole(AppRole.ROLE_ADMIN);
			
			service.createUser(user, signup.getLocale());
		} else {
			errors.add(AuthError.usernameError("UsernameTaken"));
		}
		
		return errors;
	}


	@Override
	public void generateResetToken(String login) {
		service.createPasswordResetRequest(login);
	}


	@Override
	@Transactional
	public List<AuthError> resetPassword(ResetPassword signup) {
		Objects.requireNonNull(signup);
		
		List<AuthError> errors = new ArrayList<>();
		PasswordResetRequest request = service.fetchPasswordResetRequest(signup.getToken());
		if (request == null) {
			errors.add(AuthError.usernameError("UsernameRequired"));
		} else {
			if (!StringUtils.hasText(signup.getUsername())) {
				errors.add(AuthError.usernameError("UsernameRequired"));
			} else if (!Utils.validateEmail(signup.getUsername())) {
	            errors.add(AuthError.usernameError("BadUsername"));
			}
		}

		AppUser user = service.findByEmail(signup.getUsername());
		if (user == null || !user.getId().equals(request.getUserId())) {
            errors.add(AuthError.usernameError("BadUsername"));
		}
		
		if (!PasswordSecurity.validatePassword(signup.getPassword())) {
			errors.add(AuthError.passwordError("BadPassword"));
		}
		if (!errors.isEmpty()) {
		    return errors;
		}
		
		user.setDeleted(Boolean.FALSE);
		user.setEmail(signup.getUsername());
			
		PasswordHash hash = PasswordSecurity.hashPassword(signup.getPassword());
		user.setPassword(hash.getHash());
		user.setPasswordSalt(hash.getSalt());
		user.setRole(AppRole.ROLE_ADMIN);
		
		service.updateUser(user);
		
		service.deletePasswordResetRequest(request);
			
		return errors;
	}
    
}
