package com.infinite.focus.server.auth;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.repository.query.Param;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import com.infinite.focus.server.standard.Standard;
import com.infinite.focus.server.standard.StandardRepository;
import com.infinite.focus.server.tests.SocioEmotionalTestDataWrapper;
import com.infinite.focus.server.utils.AppUtils;
import com.infinite.focus.server.utils.DateUtils;

import io.jsonwebtoken.Jwts;

/**
 * 
 * @author Saboor
 * 
 *
 */


@CrossOrigin(origins = "http://localhost:8383")
@RestController
@RequestMapping("api/auth")

public class AuthenticationController {
	
		@Value("${app.base-url}")
		private String appBaseUrl;
	
		private static final int INSTRUCTOR = 2, STUDENT = 1, ADMIN = 3, SCHOOL = 4, DISTRICT = 5;
	 	private static AccountRepository accountRepository;
		private StudentRepository studentRepository;
		private StandardRepository standardRepository;
		private InstructorRepository instructorRepository;
		private AvatarRepository avatarRepository;
	    private BCryptPasswordEncoder bCryptPasswordEncoder;
	    private AdminRepository adminRepository;
	    private SchoolRepository schoolRepository;
	    private DistrictRepository districtRepository;
	    private AccessCodeRepository accessCodeRepository;
	    
	    @Autowired
		private ConfirmationTokenRepository confirmationTokenRepository;
		
		@Autowired
		private EmailSenderService emailSenderService;
		
		@Autowired
		private PayKickStartService payKickStartService;
		
	    public AuthenticationController(DistrictRepository districtRepository, SchoolRepository schoolRepository, AdminRepository adminRepository, AvatarRepository avatarRepository,
	    		InstructorRepository instructorRepository, StudentRepository studentRepository, StandardRepository standardRepository, AccountRepository accountRepository, BCryptPasswordEncoder bCryptPasswordEncoder, AccessCodeRepository accessCodeRepository) {
	    	
	    	this.districtRepository = districtRepository;
	    	this.schoolRepository = schoolRepository;
	    	this.adminRepository = adminRepository;
	    	this.avatarRepository = avatarRepository;
	    	this.bCryptPasswordEncoder= bCryptPasswordEncoder;
	    	this.instructorRepository = instructorRepository;
	    	this.studentRepository = studentRepository;
	    	this.standardRepository = standardRepository;
	    	this.accountRepository = accountRepository;
	    	this.accessCodeRepository = accessCodeRepository;
			
		}
	    

	    @GetMapping("/get/data")   //API End point to get user data after authenticated 
	    public ResponseEntity<AccountDataWrapper> getUserData(@RequestHeader(SecurityConstants.HEADER_STRING) String token, 
	    		@RequestHeader(value = SecurityConstants.OPERATING_SYSTEM, required = false) String operating_system,
	    		@RequestHeader(value = SecurityConstants.BROWSER, required = false) String browser) throws IOException{
	    	
	     //   System.out.println("Received token:" + token);
	    	
	        	//Check token passed in header
	           if (token != null && !token.contains("undefined")) {
	               
	     //          System.out.println("Received token correctly: /get/data");
	               
	               //Parse token
	               String user = Jwts.parser()
	                       .setSigningKey(SecurityConstants.SECRET)
	                       .parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, ""))
	                       .getBody()
	                       .getSubject();
	               
	               //If token was successfully parsed
	               if (user != null) {
	                  
	     //              System.out.println(user);
	                   
	                   //Find account for parsed user name
	                   Account a = accountRepository.findByUsername(user);
	                   
	                   //If account is null return error code to client
	                   if(a == null) {
	                	   	a = accountRepository.findByUsername2(user);
	                	   	if(a == null) {
		           	   			return new ResponseEntity<AccountDataWrapper>(HttpStatus.NOT_FOUND);
		                   }
	                   }
	                   
	                   
	                   Admin ad = adminRepository.findByUserName(user);
	                   
	                  if(ad != null) {
	                	   
	                	   AccountDataWrapper adw = new AccountDataWrapper();
	                	   adw.setType(ADMIN);
	                	   
		                   //Check if user is a instructor
		                   Instructor i = instructorRepository.findByAccountId(a.getAccount_id());
		                   
		                   if(i != null) {
		                	   
		                	   adw.setI(i);
		                	   		                   
		                   }
	                	   
	                     	return new ResponseEntity<AccountDataWrapper>(adw,HttpStatus.OK);
	                   
	                   }
	                   
	                   
	                   
	                   //Check if user is a instructor
	                   Instructor i = instructorRepository.findByAccountId(a.getAccount_id());
	                   
	                   if(i != null) {
	                	   
	                	 if(i.getLicense_key() == null) {
			 	   			 throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The license key is not found!!!.");
	                	 }
	                	   
	   	   	    		 LicensesStatusResponse licensesStatusResponse = payKickStartService.getLicensesStatus(i.getLicense_key());

	   	   	    		 if(licensesStatusResponse.getData() == null) {
	   	   	    			 throw new ResponseStatusException(HttpStatus.CONFLICT, licensesStatusResponse.getMessage());
	   	   	    		 }
	 	   			    
	   	   	    		 if(licensesStatusResponse.getData().getValid() != 1) {
		 	   			    throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is not a valid license Key.");
	   	   	    		 }
	 	   			    
	   	   	    		 if(licensesStatusResponse.getData().getActive() != 1) {
		 	   			    throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is not an active license Key.");
	   	   	    		 }
	   	   	    		 
	   	   	    		 i.setOperating_system(operating_system);
	   	   	    		 i.setBrowser(browser);
	   	   	    		 instructorRepository.save(i);
	   	   	    		 
	                	   AccountDataWrapper adw = new AccountDataWrapper();
	                	   adw.setI(i);
	                	   adw.setType(INSTRUCTOR);
	                	   
	                     return new ResponseEntity<AccountDataWrapper>(adw,HttpStatus.OK);
	                   
	                   }
	                   
	                   
	                 //Check if user is a student
	                   Student s = studentRepository.findByAccountId(a.getAccount_id());
	                   
	                   if(s != null) {
	                	   
	                	   // this value temporary setted to true for bypass verification
	                	   s.setIsVerified(true);
	                	   
	                	   AccessCode accessCode = accessCodeRepository.findByStudentId(s.getStudent_id());
	                	   if(accessCode!=null) {
		                	   if(accessCode.getStatus() == Status.PENDING) {
		                		   throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,"The teacher has not verified your account.");
		                	   }
		                	   
		                	   if(accessCode.getStatus() == Status.REJECTED) {
		                		   throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,"The teacher has rejected your account.");
		                	   }
	                	   }
	                	   
	                	   
		                   Instructor instructor = instructorRepository.findByInstructorId(s.getInstructor_id());

		                   	 if(instructor.getLicense_key() == null) {
				 	   			 throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The license key is not found!!!.");
		                	 }
		                	   
		   	   	    		 LicensesStatusResponse licensesStatusResponse = payKickStartService.getLicensesStatus(instructor.getLicense_key());

		   	   	    		 if(licensesStatusResponse.getData() == null) {
		   	   	    			 throw new ResponseStatusException(HttpStatus.CONFLICT, licensesStatusResponse.getMessage());
		   	   	    		 }
		 	   			    
		   	   	    		 if(licensesStatusResponse.getData().getValid() != 1) {
			 	   			    throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is not a valid license Key.");
		   	   	    		 }
		 	   			    
		   	   	    		 if(licensesStatusResponse.getData().getActive() != 1) {
			 	   			    throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is not an active license Key.");
		   	   	    		 }
	                	   
		   	   	    	 s.setOperating_system(operating_system);
	   	   	    		 s.setBrowser(browser);
	   	   	    		 studentRepository.save(s);
	   	   	    		 
	                	   AccountDataWrapper adw = new AccountDataWrapper();
	                	   if(s.getStandard_id() > 0) {
		   	   	    			 Standard standard = s.getStandard();
			   	   	    		 adw.setStd(standard);
		   	   	    		}
	                	   adw.setS(s);
	                	   adw.setType(STUDENT);
	                	   
	                	   return new ResponseEntity<AccountDataWrapper>(adw,HttpStatus.OK);
	                   
	                   }
	                   
	                 //Check if user is a student
	                   School school = schoolRepository.findByAccountId(a.getAccount_id());
	                   
	                   if(school != null) {
	                	   
	                	   AccountDataWrapper adw = new AccountDataWrapper();
	                	   adw.setSchool(school);
	                	   adw.setType(SCHOOL);
	                	   
	                     	return new ResponseEntity<AccountDataWrapper>(adw,HttpStatus.OK);
	                   
	                   }
	                   
	                   //Check if user is a student
	                   District district = districtRepository.findByAccountId(a.getAccount_id());
	                   
	                   if(district != null) {
	                	   
	                	   AccountDataWrapper adw = new AccountDataWrapper();
	                	   adw.setDistrict(district);
	                	   adw.setType(DISTRICT);
	                	   
	                     	return new ResponseEntity<AccountDataWrapper>(adw,HttpStatus.OK);
	                   
	                   }
	                   	                  
	           		return new ResponseEntity<AccountDataWrapper>(HttpStatus.NOT_FOUND);
		
	               }
	               
	         		return new ResponseEntity<AccountDataWrapper>(HttpStatus.NOT_FOUND);

	           }else{
	        	   		return new ResponseEntity<AccountDataWrapper>(HttpStatus.NOT_FOUND);
	           }          
	    }
	    
	    @PostMapping("/sign-up/instructor")   //API End point for POST request to register a new company
	    @ResponseBody
	    public ResponseEntity<Instructor> signUpInstructor(@RequestBody InstructorSignUpRequest request) throws IOException { //Capture data sent in Request Body
	    	
	    	Account a = accountRepository.findByUsername(request.getUsername()); //Check for existing account under user name
	    	 
	    	if(a != null) {
	    		return new ResponseEntity<Instructor>(HttpStatus.CONFLICT); //if account exists return error
	    	}
	    	 
	    	checkLicenseKeyStatus(request.getInstructor().getLicense_key());
		    
	    	//Create and save new base account
	    	Account account = new Account();
	        account.setPassword(bCryptPasswordEncoder.encode(request.getPassword())); //Encode password
	        account.setUsername(request.getUsername());
	        a = accountRepository.save(account); //Write to Table
	                

	        
	    	//Create and save new company
	    	Instructor instructor = new Instructor();
	    	instructor.setFirst_name(request.getInstructor().getFirst_name());
	    	instructor.setLast_name(request.getInstructor().getLast_name());
	    	instructor.setAccount_id(a.getAccount_id());
	    	instructor.setSchool_id(request.getInstructor().getSchool_id());
	    	instructor.setGrade_id(request.getInstructor().getGrade_id());
	    	instructor.setStandard_id(request.getInstructor().getStandard_id());
	    	instructor.setLicense_key(request.getInstructor().getLicense_key());
	    	instructor.setState(request.getInstructor().getState());
	    	instructor.setCity(request.getInstructor().getCity());
	    	
	        Boolean isUnique = false;	        
	        
	        //Loop is a key that is unique is found
	        while(!isUnique) {
	        
	        //Generate possible registration code	
	        String possible_code = KeyGenerator.generateUniqueAuthCode();
	        
	        //Check to see if instructor exists with possible registration code
	        Instructor x = instructorRepository.findByRegistrationCode(possible_code);
	        
	        //If profile does not already exist then set auth code and end loop
	        if(x == null) {
	        	isUnique = true;
	        	instructor.setRegistration_code(possible_code);
	        }else {
	        	isUnique = false;
	        }
	        
	        }
	    	
	        Instructor i = instructorRepository.save(instructor); //Write to Table
	        
	        return new ResponseEntity<Instructor>(i,HttpStatus.OK);

	    }
	    
	    @PostMapping("/sign-up/district")   //API End point for POST request to register a new company
	    @ResponseBody
	    public ResponseEntity<District> signUpDistrict(@RequestBody DistrictSignUpRequest request) { //Capture data sent in Request Body
	    	
	    	 Account a = accountRepository.findByUsername(request.getUsername()); //Check for existing account under user name
	    	 
	    	 if(a != null) {
	     		return new ResponseEntity<District>(HttpStatus.CONFLICT); //if account exists return error
	    	 }
	    	    
	    	//Create and save new base account
	    	Account account = new Account();
	        account.setPassword(bCryptPasswordEncoder.encode(request.getPassword())); //Encode password
	        account.setUsername(request.getUsername());
	        a = accountRepository.save(account); //Write to Table
	                
	        District d = new District();
	        d.setDistrict_name(request.getDistrict().getDistrict_name());
	        d.setCity(request.getDistrict().getCity());
	        d.setState(request.getDistrict().getState());
	        d.setAccount_id(a.getAccount_id());
	    	
	    	Boolean isUnique = false;	        
		        
		        //Loop is a key that is unique is found
		        while(!isUnique) {
		        
		        //Generate possible registration code	
		        String possible_code = KeyGenerator.generateUniqueAuthCode();
		        
		//        System.out.println("Possible code is " + possible_code);

		        
		        //Check to see if instructor exists with possible registration code
		        District x = districtRepository.findByRegistrationCode(possible_code);
		        
		        //If profile does not already exist then set auth code and end loop
		        if(x == null) {
		        	isUnique = true;
		        	d.setRegistration_code(possible_code);
	//	        	System.out.println("District registration code is " + d.getRegistration_code());
		        }else {
		        	isUnique = false;
		        }
		        
		        }
	    	
		   districtRepository.save(d); //Write to Table
	        
	        return new ResponseEntity<District>(d,HttpStatus.OK);

	    }
	    
	    @PostMapping("/sign-up/school")   //API End point for POST request to register a new company
	    @ResponseBody
	    public ResponseEntity<School> signUpSchool(@RequestBody SchoolSignUpRequest request) throws IOException { //Capture data sent in Request Body
	    	
	    	 Account a = accountRepository.findByUsername(request.getUsername()); //Check for existing account under user name
	    	 
	    	 if(a != null) {
	     		return new ResponseEntity<School>(HttpStatus.CONFLICT); //if account exists return error
	    	 }
	    	    
	    	checkLicenseKeyStatus(request.getSchool().getLicense_key());
			    
	    	//Create and save new base account
	    	Account account = new Account();
	        account.setPassword(bCryptPasswordEncoder.encode(request.getPassword())); //Encode password
	        account.setUsername(request.getUsername());
	        a = accountRepository.save(account); //Write to Table
	                
	        
	        School s = new School();
	        s.setAddress(request.getSchool().getAddress());
	        s.setSchool_name(request.getSchool().getSchool_name());
	        s.setDistrict_id(request.getSchool().getDistrict_id());
	        s.setAccount_id(a.getAccount_id());
	        s.setLicense_key(request.getSchool().getLicense_key());
	    	
	    	Boolean isUnique = false;	        
		        
		        //Loop is a key that is unique is found
		        while(!isUnique) {
		        
		        //Generate possible registration code	
		        String possible_code = KeyGenerator.generateUniqueAuthCode();
		        
		//        System.out.println("Possible code is " + possible_code);
		        
		        //Check to see if instructor exists with possible registration code
		        School x = schoolRepository.findByRegistrationCode(possible_code);
		        
		        //If profile does not already exist then set auth code and end loop
		        if(x == null) {
		        	isUnique = true;
		        	s.setRegistration_code(possible_code);
		 //       	System.out.println("School registration code is " + s.getRegistration_code());
		        }else {
		        	isUnique = false;
		        }
		        
		        }
	    	
		   schoolRepository.save(s); //Write to Table
	        
	        return new ResponseEntity<School>(s,HttpStatus.OK);

	    }
	    
	    @GetMapping("/get/all/schools")   
	    public ResponseEntity<List<School>> getAllSchools(){
	    	
    	   	return new ResponseEntity<List<School>>(schoolRepository.findAll(), HttpStatus.OK);
	    		    	
	    }
	    
	    @GetMapping("/get/all/districts")   
	    public ResponseEntity<List<District>> getAllDistricts(){
	    	
    	   	return new ResponseEntity<List<District>>(districtRepository.findAll(), HttpStatus.OK);
	    		    	
	    }
	    
	    @PostMapping("/import/students") // API End point for POST request to register a new company
		@ResponseBody
		public ResponseEntity<List<Student>> importStudents(@RequestBody List<StudentsSignUpRequest> students)
				throws IOException { // Capture data sent in Request Body
			
			
			List<Student> registredStudetns = new ArrayList<Student>();
			
			for (StudentsSignUpRequest request : students) {
				
				// Create and save new base account
				Account account = new Account();
				account.setPassword(bCryptPasswordEncoder.encode(request.getPassword())); // Encode password

				Account a = accountRepository.save(account); // Write to Table
				
				
				studentRepository.insertStudent(a.getAccount_id(), 0L, 0L, "", request.getFirst_name(), "", 0L, 0L, request.getLast_name(), false, 0L, null, false, null, null);
		    	
		        Student s = studentRepository.getLastRegisteredStudent(); //Write to Table
				
				registredStudetns.add(s);
			}

			return new ResponseEntity<List<Student>>(registredStudetns, HttpStatus.OK);

		}
	    
		@PostMapping("/first/sign-in/student") // API End point for POST request to register a new company
		@ResponseBody
		public ResponseEntity<Student> firstSignInStudent(@RequestBody StudentsSignUpRequest request)
				throws IOException { // Capture data sent in Request Body

			List<Student> students = studentRepository.findStudentByFirstNameAndLastName(request.getFirst_name(),
					request.getLast_name());
			
			if(AppUtils.isNotNullOrEmpty(students)) { 

				for(Student student : students) {
					Account a = accountRepository.findByAccountId(student.getAccount_id());
					if(bCryptPasswordEncoder.matches(request.getPassword(), a.getPassword())) {
						return new ResponseEntity<Student>(student, HttpStatus.OK);
					}
				}

	    	}
			
			return new ResponseEntity<Student>(HttpStatus.NOT_FOUND);

		}
	    
		@PostMapping("/first/sign-in/update/student-details") // API End point for POST request to register a new company
		@ResponseBody
		public ResponseEntity<Student> firstSignInUpdateStudentDetails(@RequestBody StudentSignUpRequest request)
				throws IOException { // Capture data sent in Request Body

			Instructor instructor = instructorRepository.findByInstructorId(request.getStudent().getInstructor_id());

			if (instructor.getLicense_key() == null) {
				throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The license key is not found!!!.");
			}

			LicensesStatusResponse licensesStatusResponse = payKickStartService
					.getLicensesStatus(instructor.getLicense_key());

			if (licensesStatusResponse.getData() == null) {
				throw new ResponseStatusException(HttpStatus.CONFLICT, licensesStatusResponse.getMessage());
			}

			if (licensesStatusResponse.getData().getValid() != 1) {
				throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is not a valid license Key.");
			}

			if (licensesStatusResponse.getData().getActive() != 1) {
				throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is not an active license Key.");
			}

			Account a = accountRepository.findByUsername(request.getUsername()); // Check for existing account under
																					// user name
			Account b = accountRepository.findByUsername2(request.getUsername2()); // Check for existing account under
																					// user name 2

			if (a != null) {
				throw new ResponseStatusException(HttpStatus.CONFLICT, "The email address is already exist!!!"); // if
																													// account
																													// exists
																													// return
																													// error
			}
			if (b != null) {
				throw new ResponseStatusException(HttpStatus.CONFLICT, "The username is already exist!!!"); // if
																											// account
																											// exists
																											// return
																											// error
			}

			int age = DateUtils.getAge(request.getStudent().getDate_of_birth());
			if (age < 4 || age > 19) {
				throw new ResponseStatusException(HttpStatus.CONFLICT, "The age must be 4-19.");
			}

			// Create and save new base account
			Account account = accountRepository.findByAccountId(request.getStudent().getAccount_id());
			
			if (account == null) {
				throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Account not found!!!");
			}
			
			account.setUsername(request.getUsername());
			account.setUsername2(request.getUsername2());

			
			a = accountRepository.save(account); // Write to Table

			// Create and save new company
			Avatar avatar = avatarRepository.getOne(request.getStudent().getAvatar_id());

			Standard std = standardRepository.getOne(request.getStudent().getStandard_id());

			Student student = studentRepository.findByStudenttId(request.getStudent().getStudent_id());
			
			if (student == null) {
				throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Student not found!!!");
			}
			
			student.setFirst_name(request.getStudent().getFirst_name());
			student.setLast_name(request.getStudent().getLast_name());
			student.setInstructor_id(request.getStudent().getInstructor_id());
			student.setAccount(a);
			student.setAvatar(avatar);
			// student.setAge(age);
			student.setDate_of_birth(request.getStudent().getDate_of_birth());
			student.setEthnicity(request.getStudent().getEthnicity());
			student.setGender(request.getStudent().getGender());
			student.setGrade_id(request.getStudent().getGrade_id());
			student.setStandard(std);
			student.setAppType(request.getStudent().getAppType());
			student.setOperating_system(request.getStudent().getOperating_system());
			student.setBrowser(request.getStudent().getBrowser());

			Student s = studentRepository.save(student); // Write to Table
			Instructor i = instructorRepository.findByInstructorId(request.getStudent().getInstructor_id());

			AccessCode acce = accessCodeRepository.findByStudentIdAndInstructorId(s.getStudent_id(),
					i.getInstructor_id());
			if (acce == null) {
				AccessCode accessCode = new AccessCode(s, i);
				accessCodeRepository.save(accessCode);
			}
			
			return new ResponseEntity<Student>(s, HttpStatus.OK);

		}
		
	    @PostMapping("/sign-up/student")   //API End point for POST request to register a new company
	    @ResponseBody
	    public ResponseEntity<Student> signUpCompany(@RequestBody StudentSignUpRequest request) throws IOException { //Capture data sent in Request Body
	    	
	    	Instructor instructor = instructorRepository.findByInstructorId(request.getStudent().getInstructor_id());

          	if(instructor.getLicense_key() == null) {
          		throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The license key is not found!!!.");
          	}
       	   
 	    	LicensesStatusResponse licensesStatusResponse = payKickStartService.getLicensesStatus(instructor.getLicense_key());

 	    	if(licensesStatusResponse.getData() == null) {
 	    	   throw new ResponseStatusException(HttpStatus.CONFLICT, licensesStatusResponse.getMessage());
 	    	}
			    
 	    	if(licensesStatusResponse.getData().getValid() != 1) {
   			   throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is not a valid license Key.");
 	    	}
			    
 	    	if(licensesStatusResponse.getData().getActive() != 1) {
   			   throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is not an active license Key.");
 	    	}
 	    	
	    	 Account a = accountRepository.findByUsername(request.getUsername()); //Check for existing account under user name
	    	 Account b = accountRepository.findByUsername2(request.getUsername2()); //Check for existing account under user name 2

	    	 if(a != null) {
	    		 throw new ResponseStatusException(HttpStatus.CONFLICT, "The email address is already exist!!!"); //if account exists return error
	    	 }
	    	 if(b != null) {
	    		 throw new ResponseStatusException(HttpStatus.CONFLICT, "The username is already exist!!!"); //if account exists return error
		     }
	    	 
		int age = DateUtils.getAge(request.getStudent().getDate_of_birth());
		if (age < 4 || age > 19) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, "The age must be 4-19.");
		}
	    	 
	    	//Create and save new base account
	    	Account account = new Account();
	        account.setPassword(bCryptPasswordEncoder.encode(request.getPassword())); //Encode password
	        account.setUsername(request.getUsername());
	        account.setUsername2(request.getUsername2());

	        a = accountRepository.save(account); //Write to Table
	                

	        
	    	//Create and save new company
	        Avatar avatar = avatarRepository.getOne(request.getStudent().getAvatar_id());
	        
	        Standard std = standardRepository.getOne(request.getStudent().getStandard_id());

	    	Student student = new Student();
	    	student.setFirst_name(request.getStudent().getFirst_name());
	    	student.setLast_name(request.getStudent().getLast_name());
	    	student.setInstructor_id(request.getStudent().getInstructor_id());
	    	student.setAccount(a);
	    	student.setAvatar(avatar);
	    	//student.setAge(age);
	    	student.setDate_of_birth(request.getStudent().getDate_of_birth());
	    	student.setEthnicity(request.getStudent().getEthnicity());
	    	student.setGender(request.getStudent().getGender());
	    	student.setGrade_id(request.getStudent().getGrade_id());
	    	student.setStandard(std);
	    	student.setAppType(request.getStudent().getAppType());
	    	student.setOperating_system(request.getStudent().getOperating_system());
	    	student.setBrowser(request.getStudent().getBrowser());
	    	
	        Student s = studentRepository.save(student); //Write to Table
	        Instructor i = instructorRepository.findByInstructorId(request.getStudent().getInstructor_id());
	       
			AccessCode acce = accessCodeRepository.findByStudentIdAndInstructorId(s.getStudent_id(),
					i.getInstructor_id());
			if (acce == null) {
				AccessCode accessCode = new AccessCode(s, i);
				accessCodeRepository.save(accessCode);
			}
  
	        return new ResponseEntity<Student>(s,HttpStatus.OK);

	    }
	    
	    @GetMapping("/search/school")   
	    public ResponseEntity<School> searchForSchool(@RequestParam(value="code", defaultValue="aEn24") String code){
	    	
	    	School s = schoolRepository.findByRegistrationCode(code);
	    	
	    	if(s == null) { 

	    		return new ResponseEntity<School>(HttpStatus.NOT_FOUND);
	    	}
	    	
	    	return new ResponseEntity<School>(schoolRepository.findByRegistrationCode(code),HttpStatus.OK);
	    	
	    }
	    
	    @GetMapping("/search/district")   
	    public ResponseEntity<District> searchForDistrict(@RequestParam(value="code", defaultValue="aEn24") String code){
	    	
	    	District d = districtRepository.findByRegistrationCode(code);
	    	
	    	if(d == null) { 

	    		return new ResponseEntity<District>(HttpStatus.NOT_FOUND);
	    	}
	    	
	    	return new ResponseEntity<District>(districtRepository.findByRegistrationCode(code),HttpStatus.OK);
	    	
	    }
	    
	    
	    @GetMapping("/search/instructor")   
	    public ResponseEntity<Instructor> searchForInstructor(@RequestParam(value="code", defaultValue="aEn24") String code){
	    	
	    	Instructor i = instructorRepository.findByRegistrationCode(code);
	    	
	    	if(i == null) { 

	    		return new ResponseEntity<Instructor>(HttpStatus.NOT_FOUND);
	    	}
	    	
	    	return new ResponseEntity<Instructor>(instructorRepository.findByRegistrationCode(code),HttpStatus.OK);
	    	
	    }
	    
	    @GetMapping("/search/instructorWithGradeId")   
	    public ResponseEntity<Instructor> searchForInstructorWithGrade(@RequestParam(value="code", defaultValue="aEn24") String code, @RequestParam(value="grade_id", defaultValue="aEn24") Integer grade_id){
	    	
	    	Instructor i = instructorRepository.findByRegistrationCodeAndGradeId(code,grade_id);
	    	
	    	if(i == null) { 
	   			throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Teacher could not be found");
	    	}
	    	
	    	return new ResponseEntity<Instructor>(instructorRepository.findByRegistrationCode(code),HttpStatus.OK);
	    	
	    }
	    
	    @GetMapping("/set/avatar")   
	    public ResponseEntity<Student> searchForInstructor(@RequestParam(value="student_id", defaultValue="aEn24") long student_id, @RequestParam(value="avatar_id", defaultValue="aEn24") long avatar_id){
	    	
	    	Student s =  studentRepository.getOne(student_id);
	    	
	    	Avatar a = avatarRepository.getOne(avatar_id);
	    	
	    	if(a == null) { 

	    		return new ResponseEntity<Student>(HttpStatus.NOT_FOUND);
	    	}
	    	
	    	
	    	s.setAvatar_id(avatar_id);
	    	
	    	return new ResponseEntity<Student>(studentRepository.save(s),HttpStatus.OK);
	    	
	    }
	    
	    
	    @GetMapping("/get/avatars")   
	    public ResponseEntity<List<Avatar>> getAvatars(){
	    	
	    	return new ResponseEntity<List<Avatar>>(avatarRepository.findAll(),HttpStatus.OK);
	    	
	    }
	    
	   
	    @GetMapping(value="/check-username-availability")
	  	public ResponseEntity<Message> checkUsernameAvailability(@RequestParam(value="username") String username) {
	  			Account account = accountRepository.findByUsername(username);
	  			
	  			if(account != null) {
	  				throw new ResponseStatusException(HttpStatus.CONFLICT,"This email already registered.");
				} else {	
					Message message = new Message("This email not registred.");

					return new ResponseEntity<Message>( message,HttpStatus.OK);
				}
	    }
	    
	    @GetMapping(value="/check-username2-availability")
	  	public ResponseEntity<Message> checkUsername2Availability(@RequestParam(value="username2") String username2) {
	  			Account existingUser = accountRepository.findByUsername2(username2);
	  			
	  			if(existingUser != null) {
	  				throw new ResponseStatusException(HttpStatus.CONFLICT,"This user name is not available.");
				} else {	
					Message message = new Message("This user name is available.");

					return new ResponseEntity<Message>( message,HttpStatus.OK);
				}
	    }
	    
	    @PostMapping(value="/check-licenses-key-status")
	  	public ResponseEntity<Message> checkLicenseKeyStatus(@RequestBody CheckLicenseKeyStatusRequest request) throws IOException {
	    	 	
	    		checkLicenseKeyStatus(request.getLicense_key());
			    
	    		Message message = new Message("The license key is an active license Key.");

				return new ResponseEntity<Message>( message,HttpStatus.OK);
	    }
	    
	private void checkLicenseKeyStatus(String license_key) throws IOException {

		// Check to see if instructor exists with possible license key
		List<Instructor> instructors = instructorRepository.findByLicenseKey(license_key);

		if (!(instructors == null || instructors.isEmpty())) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is already exists.");
		}

		// Check to see if school exists with possible license key
		List<School> schools = schoolRepository.findByLicenseKey(license_key);

		if (!(schools == null || schools.isEmpty())) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, "The license key is already exists.");
		}

		isActiveAndValidLicenseKey(license_key);
	}

	private void isActiveAndValidLicenseKey(String license_key) throws IOException {
		
		LicensesStatusResponse licensesStatusResponse = payKickStartService.getLicensesStatus(license_key);
	    
		   
	    if(licensesStatusResponse.getData() == null) {
	    	throw new ResponseStatusException(HttpStatus.CONFLICT,licensesStatusResponse.getMessage());
    	}
	    
	    if(licensesStatusResponse.getData().getValid() != 1) {
	    	throw new ResponseStatusException(HttpStatus.CONFLICT,"The license key is not a valid license Key.");
    	}
	    
	    if(licensesStatusResponse.getData().getActive() != 1) {
	    	throw new ResponseStatusException(HttpStatus.CONFLICT,"The license key is not an active license Key.");
    	}
	}
	    
	@PostMapping(value = "/is-active-user")
	public ResponseEntity<Message> isActiveUser(@RequestHeader(SecurityConstants.HEADER_STRING) String token)
			throws IOException {

		// System.out.println("Received token:" + token);

		// Check token passed in header
		if (token != null && !token.contains("undefined")) {

			// System.out.println("Received token correctly: /check-licenses_key-status");

			// Parse token
			String user = Jwts.parser().setSigningKey(SecurityConstants.SECRET)
					.parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, "")).getBody().getSubject();

			// If token was successfully parsed
			if (user != null) {

				// System.out.println(user);

				// Find account for parsed user name
				Account a = accountRepository.findByUsername(user);

				// If account is null return error code to client
				if (a == null) {
					throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found!!!");
				}

				String license_key = null;

				Instructor instructor = instructorRepository.findByAccountId(a.getAccount_id());

				// If account is null return error code to client
				if (instructor == null) {
					
					School school = schoolRepository.findByAccountId(a.getAccount_id());
					
					if (school == null) {
						throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found!!!");
					} else {
						license_key = school.getLicense_key();
					}
				} else {
					license_key = instructor.getLicense_key();
				}

				if (license_key == null) {
					throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The license key is not found!!!.");
				}

				isActiveAndValidLicenseKey(license_key);
			    
				Message message = new Message("The license key is an active license Key.");

				return new ResponseEntity<Message>(message, HttpStatus.OK);
			}
		}

		throw new NotFoundException("User not found!!!");
	}
	    
	    @PostMapping(value="/forgot-password")
		public ResponseEntity<Message> forgotUserPassword(@RequestParam(value="username") String username) {
	    	
			Account account = accountRepository.findByUsername(username);
			
			String errorMessage = "This email does not exist!";
			
			if(account == null) {
				 account = accountRepository.findByUsername2(username);
				 errorMessage = "This email or username does not exist!";
			}
			
			if(account == null) {
				throw new NotFoundException(errorMessage);
			} else  {
				// create token
				ConfirmationToken confirmationToken = new ConfirmationToken(account);
				
				// save it
				confirmationTokenRepository.save(confirmationToken);
				
				//final String baseUrl = "http://3.21.52.170/#/reset/password/";//ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
				//final String baseUrl = "https://infinitefocus.app/#/reset/password/";//ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
				final String baseUrl = appBaseUrl+"#/reset/password/";//ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();

				// create the email
				SimpleMailMessage mailMessage = new SimpleMailMessage();
				mailMessage.setTo(account.getUsername());
				mailMessage.setSubject("Reset Your Infinite Focus Schools Password");
				mailMessage.setText("Forgot your password? Let's get you a new one.\r\n" + 
						"We got a request to change the password for your account.\r\n" + 
						"\r\n\n\n" + 
						"If you don't want to reset your password, you can ignore this email.\r\n" + 
						"\r\n\n\n" + 
						"Reset Password " + baseUrl + confirmationToken.getConfirmationToken()+
						"\r\n\n\n" + 
						"Thanks,\r\n" + 
						"Infinite Focus Schools");
				emailSenderService.sendEmail(mailMessage);
				
				Message message = new Message("Request to reset password received. Check your inbox for the reset link.");
				
				return new ResponseEntity<Message>( message,HttpStatus.OK);


			} 			
		}
	    
	    @PostMapping(value="/confirm-reset")
		public ResponseEntity<Message> validateResetToken(@RequestBody ResetPasswordRequest resetPasswordRequest)
		{
			ConfirmationToken token = confirmationTokenRepository.findByConfirmationToken(resetPasswordRequest.getToken());
			
			if(token != null) {
			   long diff = new Date().getTime() - token.getCreatedDate().getTime();
			       
			   long diffMinutes = diff / (60 * 1000);// % 60;

			   if (diffMinutes > 15) {
				   throw new TimeOutException("The link has been expired." + diffMinutes);
			   }
		        
				Message message = new Message("The tokne matched.");
				return new ResponseEntity<Message>( message,HttpStatus.OK);

			} else {
				throw new NotFoundException("The tokne not found.");
			}
		}	

		/**
		 * Receive the token from the link sent via email and display form to reset password
		 */
	    @PostMapping(value = "/reset-password")
		public ResponseEntity<Message> resetUserPassword(@RequestBody ResetPasswordRequest resetPasswordRequest) {
			ConfirmationToken token = confirmationTokenRepository.findByConfirmationToken(resetPasswordRequest.getToken());
			 
			if(token == null) {
				throw new NotFoundException("The tokne not found.");
			}
			
			String username = token.getAccount().getUsername();
			
			if(username == null || username.isEmpty()) {
				throw new NotFoundException("The email not found.");
			} else {
				 long diff = new Date().getTime() - token.getCreatedDate().getTime();
			       
				 long diffMinutes = diff / (60 * 1000);// % 60;

				 if (diffMinutes > 15) {
					   throw new TimeOutException( "The link has been expired." + diffMinutes);
				 }		
				 
				// use email to find user
				Account account = accountRepository.findByUsername(username);
				account.setPassword(bCryptPasswordEncoder.encode(resetPasswordRequest.getPassword())); //Encode password
				accountRepository.save(account);
				
				confirmationTokenRepository.delete(token);
				
				Message message = new Message("Password successfully reset. You can now log in with the new credentials.");
				return new ResponseEntity<Message>( message,HttpStatus.OK);
			} 
		}

	   public static boolean isValid(String email) {
	        String regex = "^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$";
	        return email.matches(regex);
	   }	   
	   
	   @PostMapping(value = "/change-password")
	   public ResponseEntity<Message> changePassword(@RequestHeader(SecurityConstants.HEADER_STRING) String token, @RequestBody ChangePasswordRequest changePasswordRequest) {
	    	
	    		//Check token passed in header
	           if (token != null && !token.contains("undefined")) {
	   
	               
	               //Parse token
	               String user = Jwts.parser()
	                       .setSigningKey(SecurityConstants.SECRET)
	                       .parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, ""))
	                       .getBody()
	                       .getSubject();
	               
	               //If token was successfully parsed
	               if (user != null) {
	                  
	                   
	                   //Find account for parsed user name
	                   Account account = accountRepository.findByUsername(user);
	                   
	                   //If account is null return error code to client
	                   if(account == null) {
	                	   account = accountRepository.findByUsername2(user);
	                	   	if(account == null) {
	                	   		throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Accout not found.");
		                   }
	                   }
	                 
	                   if(changePasswordRequest == null) {
	                	   throw new ResponseStatusException(HttpStatus.CONFLICT, "The request params does not match.");
	                   }
	                	
	                   changePasswordRequest.validatePassword(bCryptPasswordEncoder, account.getPassword());
	                			
	                   account.setPassword(bCryptPasswordEncoder.encode(changePasswordRequest.getNewPassword())); //Encode password
	                   accountRepository.save(account);
	      					      				
	                   Message message = new Message("Password successfully reset. You can now log in with the new credentials.");
	                   return new ResponseEntity<Message>( message,HttpStatus.OK);
	                  
	               } 
	           } 
	           
	           throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "");
	    }
	   
	public static boolean isAuthenticated(String token) {

		if (token != null && !token.contains("undefined")) {

			// Parse token
			String user = Jwts.parser().setSigningKey(SecurityConstants.SECRET)
					.parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, "")).getBody().getSubject();

			// If token was successfully parsed
			if (user != null) {

				// System.out.println(user);

				// Find account for parsed user name
				Account a = accountRepository.findByUsername(user);

				// If account is null return error code to client
				// If account is null return error code to client
				if (a == null) {
					a = accountRepository.findByUsername2(user);
					if (a == null) {
						throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "The user not found!!!.");

					}
				}

				return true;
			}

			throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "The user not found!!!.");

		} else {
			throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
		}
	}
}
