package com.infinite.focus.server.students;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;

import com.cloudinary.Cloudinary;
import com.cloudinary.utils.ObjectUtils;
import com.infinite.focus.server.auth.AccessCode;
import com.infinite.focus.server.auth.AccessCodeRepository;
import com.infinite.focus.server.auth.Account;
import com.infinite.focus.server.auth.AccountDataWrapper;
import com.infinite.focus.server.auth.AccountRepository;
import com.infinite.focus.server.auth.Avatar;
import com.infinite.focus.server.auth.AvatarRepository;
import com.infinite.focus.server.auth.Instructor;
import com.infinite.focus.server.auth.InstructorRepository;
import com.infinite.focus.server.auth.LicensesStatusResponse;
import com.infinite.focus.server.auth.Message;
import com.infinite.focus.server.auth.NotFoundException;
import com.infinite.focus.server.auth.SecurityConstants;
import com.infinite.focus.server.auth.Status;
import com.infinite.focus.server.auth.Student;
import com.infinite.focus.server.auth.StudentRepository;
import com.infinite.focus.server.auth.service.AuthService;
import com.infinite.focus.server.fcm.StudentPushNotification;
import com.infinite.focus.server.fcm.StudentPushNotificationRepository;
import com.infinite.focus.server.home.DailyAppreciationRepository;
import com.infinite.focus.server.home.MoodUpdateRepository;
import com.infinite.focus.server.home.QuoteRepository;
import com.infinite.focus.server.standard.Standard;
import com.infinite.focus.server.standard.StandardRepository;
import com.infinite.focus.server.students.service.StudentService;
import com.infinite.focus.server.utils.AppUtils;
import com.infinite.focus.server.utils.DateUtils;
import com.infinite.focus.server.wall.TopPicks;
import com.infinite.focus.server.wall.WallPost;
import com.infinite.focus.server.wall.WallPostAttachment;
import com.infinite.focus.server.wall.WallPostAttachmentRepository;
import com.infinite.focus.server.wall.WallPostRepository;
import com.infinite.focus.server.wall.WallPostRequestRepository;
import com.infinite.focus.server.wall.WallPostWrapper;

import io.jsonwebtoken.Jwts;

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

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

public class StudentController {

	@Autowired
	AuthService authService;
	
	@Autowired
	StudentService studentService;
	
	private AccountRepository accountRepository;
	private StudentRepository studentRepository;
	private StudentPagingAndSortingRepository studentPagingAndSortingRepository;
	private InstructorRepository instructorRepository;
	private AvatarRepository avatarRepository;
	private BCryptPasswordEncoder bCryptPasswordEncoder;
	private WallPostRepository wallPostRepository;
	private WallPostAttachmentRepository wallPostAttachmentRepository;
	private WallPostRequestRepository wallPostRequestRepository;
	private QuoteRepository qouteRepository;
	private MoodUpdateRepository moodUpdateRepository;
	private DailyAppreciationRepository dailyAppreciationRepository;
	private ClassRepository classRepository;
	private AccessCodeRepository accessCodeRepository;
	private StandardRepository standardRepository;

	private StudentPushNotificationRepository studentPushNotificationRepository;

	public StudentController(ClassRepository classRepository, DailyAppreciationRepository dailyAppreciationRepository,
			MoodUpdateRepository moodUpdateRepository, QuoteRepository qouteRepository,
			WallPostRepository wallPostRepository, WallPostAttachmentRepository wallPostAttachmentRepository,
			WallPostRequestRepository wallPostRequestRepository, AvatarRepository avatarRepository,
			InstructorRepository instructorRepository, StudentRepository studentRepository,
			AccountRepository accountRepository, BCryptPasswordEncoder bCryptPasswordEncoder,
			AccessCodeRepository accessCodeRepository,
			StudentPushNotificationRepository studentPushNotificationRepository,
			StudentPagingAndSortingRepository studentPagingAndSortingRepository,
			StandardRepository standardRepository) {

		this.classRepository = classRepository;
		this.qouteRepository = qouteRepository;
		this.wallPostRepository = wallPostRepository;
		this.wallPostAttachmentRepository = wallPostAttachmentRepository;
		this.wallPostRequestRepository = wallPostRequestRepository;
		this.avatarRepository = avatarRepository;
		this.bCryptPasswordEncoder = bCryptPasswordEncoder;
		this.instructorRepository = instructorRepository;
		this.studentRepository = studentRepository;
		this.accountRepository = accountRepository;
		this.moodUpdateRepository = moodUpdateRepository;
		this.dailyAppreciationRepository = dailyAppreciationRepository;
		this.accessCodeRepository = accessCodeRepository;
		this.studentPushNotificationRepository = studentPushNotificationRepository;
		this.studentPagingAndSortingRepository = studentPagingAndSortingRepository;
		this.standardRepository = standardRepository;
	}
	
	@GetMapping("/get/count")
	public ResponseEntity<Long> getCount() {
		return new ResponseEntity<Long>(studentRepository.count(), HttpStatus.OK);
	}

	@GetMapping("/get/student")
	public ResponseEntity<Student> getStudent(
			@RequestParam(value = "student_id", defaultValue = "aEn24") Long student_id) {

		Student s = new Student();

		Student z = studentRepository.getOne(student_id);

		s.setFirst_name(z.getFirst_name());
		s.setLast_name(z.getLast_name());
		s.setAvatar(z.getAvatar());

		// remove this line if you want to verify student via access_code.
		s.setIsVerified(true);

		return new ResponseEntity<Student>(s, HttpStatus.OK);
	}
	
	@GetMapping("/get/profile")
	public ResponseEntity<Student> getStudentProfile(
			@RequestParam(value = "student_id", defaultValue = "aEn24") Long student_id) {

		Student s = studentRepository.getOne(student_id);
		
		if (s == null) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The Student is not found!!!");
		}

		return new ResponseEntity<Student>(s, HttpStatus.OK);
	}

	@GetMapping("/get/class")
	public ResponseEntity<Class> getClass(@RequestParam(value = "class_id", defaultValue = "aEn24") Long class_id) {

		Class c = classRepository.getOne(class_id);

		if (c == null) {

			return new ResponseEntity<Class>(HttpStatus.NOT_FOUND);

		}

		return new ResponseEntity<Class>(c, HttpStatus.OK);

	}

	@GetMapping("/get/students/by/class")
	public ResponseEntity<List<Student>> getStudentByClass(
			@RequestParam(value = "class_id", defaultValue = "aEn24") Long class_id) {

		return new ResponseEntity<List<Student>>(studentRepository.findByClassId(class_id), HttpStatus.OK);

	}

	@PostMapping("/search/students/by/instructor/and/demographic") // API End point for POST request to register a new company
	public ResponseEntity<List<StudentClassWrapper>> searchStudentByInstructorAndDemographic(@RequestHeader(SecurityConstants.HEADER_STRING) String token,
			@RequestBody SearchStudentRequest searchStudentRequest) { 
		
		Instructor i = authService.isInstructor(token);

		if(i == null) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The instructor is not found.");
		}
		
		List<StudentClassWrapper> list = new ArrayList<>();
		
		for (Student s : studentService.searchStudentByInstructorAndDemographic(i, searchStudentRequest)) {

			if (s.getStatus() == null || s.getStatus() == Status.PENDING) {
				continue;
			}

			StudentClassWrapper w = new StudentClassWrapper();

			w.setStudent(s);

			if (classRepository.existsById(s.getClass_id())) {
				w.setParent_class(classRepository.getOne(s.getClass_id()));
			} else {
				Class c = new Class();
				c.setClass_id(0);
				c.setClass_name("");
				w.setParent_class(c);
			}

			list.add(w);

		}
		
		return new ResponseEntity<List<StudentClassWrapper>>(list, HttpStatus.OK);
	}
	
	@GetMapping("/get/students/by/instructor")
	public ResponseEntity<List<StudentClassWrapper>> getStudentByInstructor(
			@RequestParam(value = "instructor_id", defaultValue = "aEn24") Long instructor_id) {

		List<StudentClassWrapper> list = new ArrayList<>();

		for (Student s : studentRepository.findByInstructorId(instructor_id)) {

			if (s.getStatus() == null || s.getStatus() == Status.PENDING) {
				continue;
			}

			StudentClassWrapper w = new StudentClassWrapper();

			w.setStudent(s);

			if (classRepository.existsById(s.getClass_id())) {
				w.setParent_class(classRepository.getOne(s.getClass_id()));
			} else {
				Class c = new Class();
				c.setClass_id(0);
				c.setClass_name("");
				w.setParent_class(c);
			}

			list.add(w);

		}

		return new ResponseEntity<List<StudentClassWrapper>>(list, HttpStatus.OK);
	}

	@GetMapping("/get/classes/by/instructor")
	public ResponseEntity<List<Class>> getClassesByInstructor(
			@RequestParam(value = "instructor_id", defaultValue = "aEn24") Long instructor_id) {
		return new ResponseEntity<List<Class>>(classRepository.findByInstructorId(instructor_id), HttpStatus.OK);
	}

	@PostMapping("/create/class") // API End point for POST request to register a new company
	@ResponseBody
	public ResponseEntity<Class> createClass(@RequestBody Class request) { // Capture data sent in Request Body
		
		Class exist_class = classRepository.findByClassNameAndInstructorId(request.getClass_name(), request.getInstructor_id());
		
		if(exist_class != null) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, "The class is already exist.");
		}
		
		return new ResponseEntity<Class>(classRepository.save(request), HttpStatus.OK);
	}

	@PostMapping("/update/class") // API End point for POST request to register a new company
	@ResponseBody
	public ResponseEntity<Class> updateClass(@RequestBody Class request) { // Capture data sent in Request Body

		Class exist_class = classRepository.findByClassNameAndInstructorId(request.getClass_name(), request.getInstructor_id());

		if (exist_class != null) {
			if(exist_class.getClass_id() != request.getClass_id()) {
				throw new ResponseStatusException(HttpStatus.CONFLICT, "The class is already exist.");
			}
		}

		Class c = classRepository.getOne(request.getClass_id());

		if (c == null) {
			return new ResponseEntity<Class>(HttpStatus.NOT_FOUND);
		}

		c.setClass_name(request.getClass_name());
		c.setInstructor_id(request.getInstructor_id());
		c.setCreatedAt(c.getCreatedAt());

		return new ResponseEntity<Class>(classRepository.save(c), HttpStatus.OK);
	}

	@GetMapping("/delete/class") // API End point for POST request to register a new company
	public ResponseEntity<Class> deleteClass(@RequestParam(value = "class_id", defaultValue = "aEn24") Long class_id) { // Capture
																														// data
																														// sent
																														// in
																														// Request
																														// Body

		Class c = classRepository.getOne(class_id);

		if (c == null) {

			return new ResponseEntity<Class>(HttpStatus.NOT_FOUND);

		}

		classRepository.delete(c);

		return new ResponseEntity<Class>(c, HttpStatus.OK);
	}

	@GetMapping("/add/student/to/class") // API End point for POST request to register a new company
	public ResponseEntity<Student> addStudentToClass(
			@RequestParam(value = "student_id", defaultValue = "aEn24") Long student_id,
			@RequestParam(value = "class_id", defaultValue = "aEn24") Long class_id) {

		Student s = studentRepository.getOne(student_id);

		if (s == null) {
			return new ResponseEntity<Student>(HttpStatus.NOT_FOUND);
		}

		Class c = classRepository.getOne(class_id);

		if (c == null) {
			return new ResponseEntity<Student>(HttpStatus.NOT_FOUND);
		}

		s.setClass_id(c.getClass_id());

		return new ResponseEntity<Student>(studentRepository.save(s), HttpStatus.OK);
	}

	@PostMapping(value = "/verify-access-code")
	public ResponseEntity<Student> verifyAccessCodeStudent(@RequestParam(value = "student_id") long student_id,
			@RequestParam(value = "access_code") String access_code) {
		AccessCode accessCode = accessCodeRepository.findByAccessCode(access_code);
		if (accessCode != null) {
			if (accessCode.getStudent().getStudent_id() == student_id) {
				if (accessCode.getStatus() == Status.ACCEPTED) {
					Student student = accessCode.getStudent();
					student.setIsVerified(true);
					studentRepository.save(student);
					// accessCodeRepository.delete(accessCode);
					return new ResponseEntity<Student>(student, HttpStatus.OK);
				}
				return new ResponseEntity<Student>(HttpStatus.UNAUTHORIZED);
			}
			return new ResponseEntity<Student>(HttpStatus.NOT_FOUND);
		} else {
			return new ResponseEntity<Student>(HttpStatus.NOT_FOUND);
		}
	}

	@PostMapping("/set/fcm/token") // API End point for POST request to register a new company
	public ResponseEntity<Message> setFCMToken(@RequestHeader(SecurityConstants.HEADER_STRING) String token,
			@RequestParam(value = "fcm_token") String fcm_token) { // Capture data sent in Request Body

		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 (a == null) {
					a = accountRepository.findByUsername2(user);
					if (a == null) {
						throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found!!!");
					}
				}

				Student student = studentRepository.findByAccountId(a.getAccount_id());

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

				student.setFcm_token(fcm_token);

				studentRepository.save(student);

				Message message = new Message("The fcm_token setted.");

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

		throw new NotFoundException("User not found!!!");
	}

	@GetMapping("/get/notifications")
	public ResponseEntity<Page<StudentPushNotification>> getNotificationsByStudentId(
			@RequestHeader(SecurityConstants.HEADER_STRING) String token,
			@RequestParam(value = "pageNo", defaultValue = "0") Integer pageNo,
			@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
			@RequestParam(value = "sortBy", defaultValue = "student_push_notification_id") String sortBy,
			@RequestParam(value = "status", defaultValue = "PENDING") String status) {

		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 (a == null) {
					a = accountRepository.findByUsername2(user);
					if (a == null) {
						throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found!!!");
					}
				}

				Student student = studentRepository.findByAccountId(a.getAccount_id());

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

				Pageable paging = PageRequest.of(pageNo, pageSize, Sort.by(sortBy).descending());

				Page<StudentPushNotification> pagedResult = (Page<StudentPushNotification>) studentPushNotificationRepository
						.findByStudentId(student.getStudent_id(), paging);

				return new ResponseEntity<Page<StudentPushNotification>>(pagedResult, new HttpHeaders(), HttpStatus.OK);
			}
		}

		throw new NotFoundException("User not found!!!");

	}

	@GetMapping("/get/students")
	public ResponseEntity<Page<Student>> getStudents(@RequestHeader(SecurityConstants.HEADER_STRING) String token,
			@RequestParam(value = "name", defaultValue = "") String name,
			@RequestParam(value = "pageNo", defaultValue = "0") Integer pageNo,
			@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
			@RequestParam(value = "sortBy", defaultValue = "student_id") String sortBy) {

		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 (a == null) {
					throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found!!!");
				}

				Pageable paging = PageRequest.of(pageNo, pageSize, Sort.by(sortBy).ascending());

				Page<Student> pagedResult = null;

				if (name == null || name.isEmpty()) {
					pagedResult = (Page<Student>) studentPagingAndSortingRepository.findAllStudents(paging);
				} else {
					pagedResult = (Page<Student>) studentPagingAndSortingRepository.findByStudentByName(name, paging);
				}

				return new ResponseEntity<Page<Student>>(pagedResult, new HttpHeaders(), HttpStatus.OK);
			}
		}

		throw new NotFoundException("User not found!!!");
	}
	
	@GetMapping("/logout") // API End point for POST request to register a new company
	public ResponseEntity<Message> logout(@RequestHeader(SecurityConstants.HEADER_STRING) String token) { // Capture data sent in Request Body

		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 (a == null) {
					a = accountRepository.findByUsername2(user);
					if (a == null) {
						throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found!!!");
					}
				}

				Student student = studentRepository.findByAccountId(a.getAccount_id());

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

				student.setFcm_token(null);

				studentRepository.save(student);

				Message message = new Message("The user is logout successfully.");

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

		throw new NotFoundException("User not found!!!");
	}

	@PostMapping("/edit/profile") // API End point for POST request to register a new company
	public ResponseEntity<Student> editProfile(@RequestHeader(SecurityConstants.HEADER_STRING) String token,
			@RequestBody Student student) { // Capture data sent in Request Body

		Instructor i = authService.isInstructor(token);

		if(i == null) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The instructor is not found.");
		}
		
		Student s = studentRepository.getOne(student.getStudent_id());

		// If account is null return error code to client
		if (s == null) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The Student is not found!!!");
		}
		
		if (AppUtils.isNullOrEmpty(student.getFirst_name())) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, "The first name is required.");
		}

		if (AppUtils.isNullOrEmpty(student.getLast_name())) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, "The last name is required.");
		}
		
		if (AppUtils.isNullOrEmpty(student.getEthnicity())) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, "The Ethnicity is required.");
		}
		
		if (AppUtils.isNullOrEmpty(student.getGender())) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, "The Gender is required.");
		}
		
		int age = DateUtils.getAge(student.getDate_of_birth());
		
		if (age < 4 || age > 19) {
			throw new ResponseStatusException(HttpStatus.CONFLICT, "The age must be 4-19.");
		}
		
		//Create and save new company
        Avatar avatar = avatarRepository.getOne(student.getAvatar_id());
        
        Standard std = standardRepository.getOne(student.getStandard_id());

    	
    	s.setFirst_name(student.getFirst_name());
    	s.setLast_name(student.getLast_name());
    	s.setAvatar(avatar);
    	//s.setAge(age);
    	s.setDate_of_birth(student.getDate_of_birth());
    	s.setEthnicity(student.getEthnicity());
    	s.setGender(student.getGender());
    	s.setGrade_id(student.getGrade_id());
    	s.setStandard(std);
    	
    	Student stud = studentRepository.save(s); //Write to Table
    	 
    	return new ResponseEntity<Student>(stud, HttpStatus.OK);
	}
}
