package com.infinite.focus.server.dashboard;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.In;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.metamodel.SingularAttribute;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.Query;
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.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 com.cloudinary.Cloudinary;
import com.cloudinary.utils.ObjectUtils;
import com.infinite.focus.server.activity.Activity;
import com.infinite.focus.server.activity.ActivityRepository;
import com.infinite.focus.server.activity.ActivityTypeRepository;
import com.infinite.focus.server.auth.AccountRepository;
import com.infinite.focus.server.auth.AvatarRepository;
import com.infinite.focus.server.grade.Grade;
import com.infinite.focus.server.grade.GradeRepository;
import com.infinite.focus.server.auth.Instructor;
import com.infinite.focus.server.auth.InstructorRepository;
import com.infinite.focus.server.auth.InstructorSignUpRequest;
import com.infinite.focus.server.auth.Student;
import com.infinite.focus.server.auth.StudentRepository;
import com.infinite.focus.server.dashboard.features.FeaturesService;
import com.infinite.focus.server.dashboard.mooddata.MoodDataService;
import com.infinite.focus.server.dashboard.socioemotionalassessment.SocioEmotionalAssessmentService;
import com.infinite.focus.server.dashboard.stats.StatsService;
import com.infinite.focus.server.dashboard.userstats.UserStatsService;
import com.infinite.focus.server.data.AssessmentDataWrapper;
import com.infinite.focus.server.data.MoodOccurance;
import com.infinite.focus.server.home.DailyAppreciationRepository;
import com.infinite.focus.server.home.MoodUpdate;
import com.infinite.focus.server.home.MoodUpdateRepository;
import com.infinite.focus.server.home.MoodUpdate_;
import com.infinite.focus.server.home.QuoteRepository;
import com.infinite.focus.server.lessons.Lesson;
import com.infinite.focus.server.lessons.LessonRecord;
import com.infinite.focus.server.lessons.LessonRecordRepository;
import com.infinite.focus.server.lessons.LessonRecordWrapper;
import com.infinite.focus.server.lessons.LessonRepository;
import com.infinite.focus.server.standard.Standard;
import com.infinite.focus.server.standard.StandardRepository;
import com.infinite.focus.server.students.Class;
import com.infinite.focus.server.students.ClassRepository;
import com.infinite.focus.server.students.Student_;
import com.infinite.focus.server.tests.AnswerRepository;
import com.infinite.focus.server.tests.CorrectAnswerRepository;
import com.infinite.focus.server.tests.MultipleChoiceOptionRepository;
import com.infinite.focus.server.tests.QuestionRepository;
import com.infinite.focus.server.tests.SocioEmotionalTestAnswer;
import com.infinite.focus.server.tests.SocioEmotionalTestAnswerRepository;
import com.infinite.focus.server.tests.SocioEmotionalTestDataWrapper;
import com.infinite.focus.server.tests.SocioEmotionalTestQuestion;
import com.infinite.focus.server.tests.SocioEmotionalTestQuestionOption;
import com.infinite.focus.server.tests.SocioEmotionalTestQuestionOptionRepository;
import com.infinite.focus.server.tests.SocioEmotionalTestQuestionRepository;
import com.infinite.focus.server.tests.SocioEmotionalTestQuestionTypeRepository;
import com.infinite.focus.server.tests.SocioEmotionalTestResult;
import com.infinite.focus.server.tests.SocioEmotionalTestResultRepository;
import com.infinite.focus.server.tests.SocioEmotionalTestResult_;
import com.infinite.focus.server.tests.Test;
import com.infinite.focus.server.tests.TestRepository;
import com.infinite.focus.server.tests.TestResult;
import com.infinite.focus.server.tests.TestResultGrade;
import com.infinite.focus.server.tests.TestResultGradeRepository;
import com.infinite.focus.server.tests.TestResultRepository;
import com.infinite.focus.server.utils.AppUtils;
import com.infinite.focus.server.utils.DateUtils;
import com.infinite.focus.server.utils.DigitUtils;
import com.infinite.focus.server.utils.MoodUpdateUtils;
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;

/**
 * 
 * @author Saboor Created: 11/25/18
 * 
 *
 */

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

public class DashboardController {

	@Autowired
	EntityManager entityManager;
	
	@Autowired
	UserStatsService userStatsService;
	
	@Autowired
	StatsService statsService;
	
	@Autowired
	SocioEmotionalAssessmentService socioEmotionalAssessmentService;
	
	@Autowired
	MoodDataService moodDataService;
	
	@Autowired
	FeaturesService featuresService;

	private AccountRepository accountRepository;
	private StudentRepository studentRepository;
	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 TestRepository testRepository;
	private QuestionRepository questionRepository;
	private CorrectAnswerRepository correctAnswerRepository;
	private MultipleChoiceOptionRepository multipleChoiceOptionRepository;
	private AnswerRepository answerRepository;
	private TestResultRepository testResultRepository;
	private TestResultGradeRepository testResultGradeRepository;
	private LessonRepository lessonRepository;
	private GradeRepository gradeRepository;
	private StandardRepository standardRepository;
	private LessonRecordRepository lessonRecordRepository;

	private SocioEmotionalTestResultRepository socioEmotionalTestResultRepository;
	private SocioEmotionalTestQuestionTypeRepository socioEmotionalTestQuestionTypeRepository;
	private SocioEmotionalTestQuestionRepository socioEmotionalTestQuestionRepository;
	private SocioEmotionalTestQuestionOptionRepository socioEmotionalTestQuestionOptionRepository;
	private SocioEmotionalTestAnswerRepository socioEmotionalTestAnswerRepository;
	
	private ActivityRepository activityRepository;
	private ActivityTypeRepository activityTypeRepository;

	public DashboardController(SocioEmotionalTestResultRepository socioEmotionalTestResultRepository,
			LessonRecordRepository lessonRecordRepository, LessonRepository lessonRepository,
			TestResultGradeRepository testResultGradeRepository, TestResultRepository testResultRepository,
			AnswerRepository answerRepository, TestRepository testRepository, QuestionRepository questionRepository,
			CorrectAnswerRepository correctAnswerRepository,
			MultipleChoiceOptionRepository multipleChoiceOptionRepository, ClassRepository classRepository,
			DailyAppreciationRepository dailyAppreciationRepository, MoodUpdateRepository moodUpdateRepository,
			QuoteRepository qouteRepository, WallPostRepository wallPostRepository,
			WallPostAttachmentRepository wallPostAttachmentRepository,
			WallPostRequestRepository wallPostRequestRepository, AvatarRepository avatarRepository,
			InstructorRepository instructorRepository, StudentRepository studentRepository,
			AccountRepository accountRepository, GradeRepository gradeRepository, StandardRepository standardRepository,
			SocioEmotionalTestQuestionTypeRepository socioEmotionalTestQuestionTypeRepository,
			SocioEmotionalTestQuestionRepository socioEmotionalTestQuestionRepository,
			SocioEmotionalTestQuestionOptionRepository socioEmotionalTestQuestionOptionRepository,
			SocioEmotionalTestAnswerRepository socioEmotionalTestAnswerRepository,
			ActivityRepository activityRepository,
			ActivityTypeRepository activityTypeRepository,
			BCryptPasswordEncoder bCryptPasswordEncoder) {

		this.socioEmotionalTestResultRepository = socioEmotionalTestResultRepository;
		this.lessonRecordRepository = lessonRecordRepository;
		this.testResultRepository = testResultRepository;
		this.answerRepository = answerRepository;
		this.testRepository = testRepository;
		this.lessonRepository = lessonRepository;

		this.questionRepository = questionRepository;
		this.correctAnswerRepository = correctAnswerRepository;
		this.multipleChoiceOptionRepository = multipleChoiceOptionRepository;
		this.testResultGradeRepository = testResultGradeRepository;
		this.gradeRepository = gradeRepository;
		this.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.socioEmotionalTestQuestionTypeRepository = socioEmotionalTestQuestionTypeRepository;
		this.socioEmotionalTestQuestionRepository = socioEmotionalTestQuestionRepository;
		this.socioEmotionalTestQuestionOptionRepository = socioEmotionalTestQuestionOptionRepository;
		this.socioEmotionalTestAnswerRepository = socioEmotionalTestAnswerRepository;
		this.activityRepository = activityRepository;
		this.activityTypeRepository = activityTypeRepository;
	}

	@GetMapping("/get/dropDownsForAnalyticsData")
	public ResponseEntity<DropDownsForAnalyticsDataWrapper> getDropDownsForAnalyticsData() {
		return new ResponseEntity<DropDownsForAnalyticsDataWrapper>(getDropDownsForAnalyticsDataWrapper(), HttpStatus.OK);
	}
	
	private List<Integer> ages = Arrays.asList(4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19);

	@PostMapping("/get/socioEmotionalDataAnalytics")
	public ResponseEntity<Object> getSocioEmotionalDataAnalytics(
			@RequestBody GetDataDashBoardRequest request) {

		if(request.getToDate() != null) {
			
			 Calendar cal = Calendar.getInstance();
			 
			 // From Date 
			 cal.setTime(request.getFromDate());
			 cal.set(Calendar.HOUR_OF_DAY, 0);
			 cal.set(Calendar.MINUTE, 0);
			 cal.set(Calendar.SECOND, 0);
			 cal.set(Calendar.MILLISECOND, 0);
			 Date fromDate = cal.getTime();
			 request.setFromDate(fromDate);
			 
			 // To Date
			 cal.setTime(request.getToDate());
			 cal.set(Calendar.HOUR_OF_DAY, 23);
			 cal.set(Calendar.MINUTE, 59);
			 cal.set(Calendar.SECOND, 59);
			 cal.set(Calendar.MILLISECOND, 999);
			 Date toDate = cal.getTime();
			 request.setToDate(toDate);
		}
		
		switch (request.getGraphType()) {
		case OVERALL:{
			return new ResponseEntity<Object>(userStatsService.getOverall(request), HttpStatus.OK);
		}
		case TEACHERS: {
			return new ResponseEntity<Object>(userStatsService.getTeachers(request), HttpStatus.OK);
		}
		case STUDENTS: {
			return new ResponseEntity<Object>(userStatsService.getStudents(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case STUDENTS_AND_DEMOGRAPHIC_STRANDS: {
			GetDataDashBoardRequest defaultRequest = getDefaultGetDataDashBoardRequest(request);
			return new ResponseEntity<Object>(userStatsService.getStudentsAndDemographicStrands(defaultRequest), HttpStatus.OK);
		}
		case LOGIN_ATTEMPTS_STUDENTS:{
			//LinkedHashMap<String, List<LabelAndValue>> graphData = userStatsService.getLogiAttemptsStudentsData(request);
			//return new ResponseEntity<Object>(getLabelsAndDatasets(request, graphData), HttpStatus.OK);
			return new ResponseEntity<Object>(userStatsService.getLogiAttemptsStudentsData(request), HttpStatus.OK);
		}
		case LOGIN_ATTEMPTS_TEACHERS:{
			LinkedHashMap<String, List<LabelAndValue>> graphData = userStatsService.getLogiAttemptsTeachersData(getDefaultGetDataDashBoardRequest(request));
			return new ResponseEntity<Object>(getLabelsAndDatasets(request, graphData), HttpStatus.OK);
		}
		case AVERAGE_LOGIN_ATTEMPTS_TEACHERS:{
			return new ResponseEntity<Object>(userStatsService.getAverageLoginAttemptTeachers(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case PERCENTAGE_BY_GRADES:{
			return new ResponseEntity<Object>(userStatsService.getPercentageByGrades(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case TIME_SPENT_BY_STUDENTS: {
			return new ResponseEntity<Object>(statsService.getTimeSpentByStudents(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case TIME_SPENT_BY_TEACHERS: {
			return new ResponseEntity<Object>(statsService.getTimeSpentByTeachers(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case TIME_SPENT_BY_DEMOGRAPHICS: {
			LinkedHashMap<String, List<LabelAndValue>> graphData = statsService.getTimeSpentByDemographicsData(request);
			return new ResponseEntity<Object>(getLabelsAndDatasets(request, graphData), HttpStatus.OK);
		}
		case TIME_SPENT_BY_DEMOGRAPHIC: {
			GetDataDashBoardRequest defaultRequest = getDefaultGetDataDashBoardRequest(request);
			return new ResponseEntity<Object>(statsService.getTimeSpentByDemographicData(defaultRequest), HttpStatus.OK);
		}
		case TIME_SPENT_BY_GRADES: {
			LinkedHashMap<String, List<LabelAndValue>> graphData = statsService.getTimeSpentByGradesData(getDefaultGetDataDashBoardRequest(request));
			return new ResponseEntity<Object>(getLabelsAndDatasets(request, graphData), HttpStatus.OK);
		}
		case TIME_SPENT_BY_GRADE: {
			return new ResponseEntity<Object>(statsService.getTimeSpentByGradeData(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case VIDEOS:{
			return new ResponseEntity<Object>(statsService.getVideoViewList(request), HttpStatus.OK);
		}
		case VIDEOS_DEMOGRAPHIC:{
			return new ResponseEntity<Object>(statsService.getVideosDemographicData(request), HttpStatus.OK);
		}
		case SEL_STRANDS: {
			return new ResponseEntity<Object>(socioEmotionalAssessmentService.getSELStrandsGraphData(request), HttpStatus.OK);
		}
		case SINGLE_DEMOGRAPHIC_COMPARISON: {
			LinkedHashMap<String, List<LabelAndValue>> graphData = socioEmotionalAssessmentService.getSingleDemographicComparisonGraphData(request);
			return new ResponseEntity<Object>(getLabelsAndDatasets(request, graphData), HttpStatus.OK);
		}
		case DEMOGRAPHIC_COMPARISON: {
			LinkedHashMap<String, List<LabelAndValue>> graphData = socioEmotionalAssessmentService.getDemographicComparisonGraphData(request);
			return new ResponseEntity<Object>(getLabelsAndDatasets(request, graphData), HttpStatus.OK);
		}
		case TIME_SPENT_AND_SEL_STRANDS:{
			return new ResponseEntity<Object>(socioEmotionalAssessmentService.getTimeSpentAndSELStrandsGraphData(request), HttpStatus.OK);
		}
		case SELF_REPORTING: {
			return new ResponseEntity<Object>(moodDataService.getSelfReportingData(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case MOOD_DEMOGRAPHIC_COMPARISON: {
			return new ResponseEntity<Object>(moodDataService.getMoodUpdateDemographicComparisonGraphData(request), HttpStatus.OK);
		}
		case MOOD_ANALYSIS: {
			return new ResponseEntity<Object>(moodDataService.getMoodAnalysisGraphData(request), HttpStatus.OK);
		}
		case MOST_AND_LEAST_REPORTED: {
			return new ResponseEntity<Object>(moodDataService.getMostAndLeastReportedData(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case MOOD_ANALYSIS_AND_COMPETENCY_SCORE: {
			return new ResponseEntity<Object>(moodDataService.getMoodAnalysisAndCompetencyScoreGraphData(request), HttpStatus.OK);
		}
		case BY_STUDENTS_DEMOGRAPHICS:{
			return new ResponseEntity<Object>(featuresService.getByStudentsDemographicsData(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case BY_TEACHER_S_GRADES:{
			return new ResponseEntity<Object>(featuresService.getByTeachersGradesData(getDefaultGetDataDashBoardRequest(request)), HttpStatus.OK);
		}
		case MOOD_INSIGHTS:{
			return new ResponseEntity<Object>(moodDataService.getMoodInsightByInstructerId(request), HttpStatus.OK);
		}
		case STUDENT_PERCENTAGE_BY_OS:{
			return new ResponseEntity<Object>(userStatsService.getPercentageByOSForStudent(request), HttpStatus.OK);
		}
		case STUDENT_PERCENTAGE_BY_BROWSER:{
			return new ResponseEntity<Object>(userStatsService.getPercentageByBrowserForStudent(request), HttpStatus.OK);
		}
		case INSTRUCTOR_PERCENTAGE_BY_OS:{
			return new ResponseEntity<Object>(userStatsService.getPercentageByOSForInstructor(request), HttpStatus.OK);
		}
		case INSTRUCTOR_PERCENTAGE_BY_BROWSER:{
			return new ResponseEntity<Object>(userStatsService.getPercentageByBrowserForInstructor(request), HttpStatus.OK);
		}
		case PAGE_PERFORMANCE_ANALITICS:{
			return new ResponseEntity<Object>(featuresService.getPagePerformanceAnalyticsOrderByScreenId(), HttpStatus.OK);
		}
		}

		return new ResponseEntity<Object>(new Object(), HttpStatus.OK);
	}
	
	private LabelsAndDatasets getLabelsAndDatasets(GetDataDashBoardRequest request, LinkedHashMap<String, List<LabelAndValue>> graphData) {
		
		LabelsAndDatasets labelsAndDatasets = new LabelsAndDatasets();
		
		LinkedList<String> labels = new LinkedList<>();
		labels.addAll(DateUtils.getLabelsByTimeLine(request.getTimeLine()));
		labelsAndDatasets.setLabels(labels);
		
		LinkedList<LabelAndData> dataset = new LinkedList<>();
		
		for (String key : graphData.keySet()) {

			List<LabelAndValue> values = graphData.get(key);
			LinkedList<Double> data = new LinkedList<>();

			if (AppUtils.isNotNullOrEmpty(values)) {
				for (int i = 0; i < values.size(); i++) {
					data.add(DigitUtils.formatDoubleInTwoDigit(values.get(i).getValue()));
				}
			}

			LabelAndData labelAndData = new LabelAndData(key, data);
			dataset.add(labelAndData);
		}
		
		labelsAndDatasets.setDataset(dataset);
		
		return labelsAndDatasets;
	}

	private GetDataDashBoardRequest getDefaultGetDataDashBoardRequest(GetDataDashBoardRequest request) {
		
		DropDownsForAnalyticsDataWrapper dropDownsForAnalyticsDataWrapper = getDropDownsForAnalyticsDataWrapper();
		
		request.setGenders(dropDownsForAnalyticsDataWrapper.getGenders());
		//request.setAges(studentRepository.groupByAge());
		request.setAges(ages);
		request.setEthnicity(dropDownsForAnalyticsDataWrapper.getEthnicity());
		
		for(Grade grade: dropDownsForAnalyticsDataWrapper.getGrades()) {
			request.getGradeIds().add(grade.getGrade_id());
		}
		
		for(Class class_: dropDownsForAnalyticsDataWrapper.getClasses()) {
			request.getClassIds().add(class_.getClass_id());
		}
		
		return request;
	}
	
	private DropDownsForAnalyticsDataWrapper getDropDownsForAnalyticsDataWrapper() {
		DropDownsForAnalyticsDataWrapper dropDownsForAnalyticsDataWrapper = new DropDownsForAnalyticsDataWrapper();

		dropDownsForAnalyticsDataWrapper.setQuestion_types(socioEmotionalTestQuestionTypeRepository.findAll());
		dropDownsForAnalyticsDataWrapper.setGrades(gradeRepository.findAll());
		
		List<Class> classes = new ArrayList<Class>();
		
		for(Class class_ :  classRepository.findAll()) {
			Class newClass = new Class();
			newClass.setClass_id(class_.getClass_id());
			newClass.setClass_name(class_.getClass_name());
			newClass.setInstructor_id(class_.getInstructor_id());
			newClass.setCreatedAt(class_.getCreatedAt());
			classes.add(newClass);
		}
		
		if(AppUtils.isNotNullOrEmpty(classes)) {
			for(int i = 0; i < classes.size(); i++) {
				Instructor instructor = instructorRepository.getOne(classes.get(i).getInstructor_id());
				classes.get(i).setClass_name(classes.get(i).getClass_name().trim() + " (" + instructor.getFirst_name().trim() + " " + instructor.getLast_name().trim() + ")");
			}
		} 
		
		dropDownsForAnalyticsDataWrapper.setClasses(classes);
		
		return dropDownsForAnalyticsDataWrapper;
	}
	
	public static String getClassNameWithInstructorFullName(Long class_id, ClassRepository classRepository, InstructorRepository instructorRepository) {
		Class class_ = classRepository.getOne(class_id);
		Instructor instructor = instructorRepository.getOne(class_.getInstructor_id());
		String class_name = class_.getClass_name().trim() + " (" + instructor.getFirst_name().trim() + " " + instructor.getLast_name().trim() + ")";
		return class_name;
	}
}
