package tutorial.core.services.impl;

import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tutorial.core.models.entities.Account;
import tutorial.core.repositories.AccountRepo;
import tutorial.core.services.AccountService;
import tutorial.core.services.exceptions.AccountExistsException;
import tutorial.core.services.util.AccountList;

import java.util.Random;
import tutorial.core.repositories.EmailDomainRepo;
import tutorial.core.services.exceptions.EmailDomainExistsException;
import tutorial.core.services.exceptions.InvalidMailException;

/**
 * 
 * @author Xavier
 */
@Service
@Transactional
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountRepo accountRepo;
    
    @Autowired
    private EmailDomainRepo emailDomainRepo;

    final static String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    @Override
    public Account findAccount(Long id) {
        return accountRepo.findAccount(id);
    }

    @Override
    public Account createAccount(Account data) {
        Account account = accountRepo.findAccountByName(data.getName());
        /*Creates the account if an account with the same name doesn't exist*/
        if(account != null){
            throw new AccountExistsException();
        }

        String mail = data.getName();
        String[] split = mail.split("@");
        if(split.length != 2){
            throw new InvalidMailException();
        }
        int atIndex = mail.indexOf("@");
        String emailDomain = mail.substring(atIndex);
        
        Boolean isAdmin = mail.equals(accountRepo.getMailAccount());
        if(emailDomainRepo.findEmailDomainByName(emailDomain) == null){
            if (!isAdmin) {
                throw new EmailDomainExistsException();
            }
        }
        /*Administrator is given administrator rights*/
        if (isAdmin) {
            data.setAccountType(accountRepo.getAdminUserType());
        }
        Date now = new Date();
        data.setCreated(now);
        data.setActivated(now);
        data.setPassword(randomPassword(randInt(8,16)));
        return accountRepo.createAccount(data);
    }
    
    /**
    * Returns a pseudo-random number between min and max, inclusive.
    * The difference between min and max can be at most
    * <code>Integer.MAX_VALUE - 1</code>.
    *
    * @param min Minimum value
    * @param max Maximum value.  Must be greater than min.
    * @return Integer between min and max, inclusive.
    * @see java.util.Random#nextInt(int)
    */
    public static int randInt(int min, int max) {
    Random rand = new Random();
    // nextInt is normally exclusive of the top value,
    // so add 1 to make it inclusive
    int randomNum = rand.nextInt((max - min) + 1) + min;
    return randomNum;
    }

    String randomPassword(int length){
        
        Random rnd = new Random();
        StringBuilder sb = new StringBuilder(length);
        for( int i = 0; i < length; i++ ) 
            sb.append( chars.charAt( rnd.nextInt(chars.length()) ) );
        return sb.toString();
    }

    @Override
    public AccountList findAllAccounts() {
        return new AccountList(accountRepo.findAllAccounts());
    }

    @Override
    public Account findByAccountName(String name) {
        return accountRepo.findAccountByName(name);
    }
    
    @Override
    public AccountList findAllActivatedAccounts() {
        return new AccountList(accountRepo.findAllActivatedAccounts());
    }

    @Override
    public Account findActivatedByAccountName(String name) {
        return accountRepo.findActivatedAccountByName(name);
    }

    @Override
    public Boolean isAccountCorrect(String name, String password) {
        if(name == null) {
            return false;
        } else {
            Account account = this.findByAccountName(name);
            if(account == null) {
                return false;
            } else {
                if(!account.getPassword().equalsIgnoreCase(password)){
                    return false;
                }
            }
        }
        return true;
    }
    
    @Override
    public Boolean isAccountAdminByName(String name) {
        if(name == null) {
            return false;
        } else {
            Account account = this.findByAccountName(name);
            if(account == null) {
                return false;
            } else {
                if(account.getAccountType()==3){
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public void modifyPassword(String name, String password) {
        accountRepo.modifyPassword(name, password);
    }
}
