package com.infinite.focus.server.dashboard.socioemotionalassessment;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.infinite.focus.server.auth.InstructorRepository;
import com.infinite.focus.server.auth.Student;
import com.infinite.focus.server.auth.StudentRepository;
import com.infinite.focus.server.activity.Activity;
import com.infinite.focus.server.activity.Activity_;
import com.infinite.focus.server.activity.service.ActivityService;
import com.infinite.focus.server.dashboard.GetDataDashBoardRequest;
import com.infinite.focus.server.dashboard.LabelAndValue;
import com.infinite.focus.server.dashboard.LabelAndValueScore;
import com.infinite.focus.server.dashboard.TimeLine;
import com.infinite.focus.server.grade.GradeRepository;
import com.infinite.focus.server.standard.Standard;
import com.infinite.focus.server.standard.StandardRepository;
import com.infinite.focus.server.standard.Standard_;
import com.infinite.focus.server.students.ClassRepository;
import com.infinite.focus.server.students.Student_;
import com.infinite.focus.server.students.service.StudentService;
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.SocioEmotionalTestResult;
import com.infinite.focus.server.tests.SocioEmotionalTestResult_;
import com.infinite.focus.server.utils.AppUtils;
import com.infinite.focus.server.utils.DateUtils;
import com.infinite.focus.server.utils.DigitUtils;

@Service
public class SocioEmotionalAssessmentServiceImpl implements SocioEmotionalAssessmentService {
	
	@Autowired
	EntityManager entityManager;
	
	@Autowired
	ActivityService activityService;
	
	@Autowired
	SocioEmotionalTestQuestionOptionRepository socioEmotionalTestQuestionOptionRepository;
	
	@Autowired
	SocioEmotionalTestQuestionRepository socioEmotionalTestQuestionRepository;
	
	@Autowired
	SocioEmotionalTestAnswerRepository socioEmotionalTestAnswerRepository;
	
	@Autowired
	GradeRepository gradeRepository;
	
	@Autowired
	StandardRepository standardRepository;

	@Autowired
	StudentRepository studentRepository;

	@Autowired
	StudentService studentService;
	
	@Autowired
	InstructorRepository instructorRepository;

	@Autowired
	ClassRepository classRepository;

	String forwardSlash = "/";
	
	@Override
	public List<LabelAndValue> getSELStrandsGraphData(GetDataDashBoardRequest request) {

		List<Long> student_ids = studentService.getStudentByDemographics(request);
		List<SocioEmotionalTestResult> socioEmotionalTestResultList = getSocioEmotionalTestResultByStudentIds(
				student_ids, request.getTimeLine());

		LinkedHashMap<String, List<SocioEmotionalTestResult>> testResultMap = getSocioEmotionalTestResultMapByTimeLine(
				request.getTimeLine(), socioEmotionalTestResultList);

		Long socio_emotional_test_question_type_id = request.getSocio_emotional_test_question_type_id();

		List<LabelAndValue> labelAndValues = new ArrayList<>();

		if (socio_emotional_test_question_type_id == null) {
			for (String key : testResultMap.keySet()) {

				List<SocioEmotionalTestResult> values = testResultMap.get(key);

				if (!values.isEmpty()) {

					double score = 0;

					for (SocioEmotionalTestResult test : values) {
						score = score + test.getScore();
						System.out.println("test id " + test.getSocio_emotional_test_result_id() + " test score " + test.getScore() + " score = " + score);
					}

					double avgScore = score / values.size();
					System.out.println("Key"+ key + "avgScore  " + avgScore + "  score = " + score + " size (count) = " + values.size() );
					labelAndValues.add(new LabelAndValue(key, DigitUtils.formatDoubleInTwoDigit(avgScore)));

				} else {
					labelAndValues.add(new LabelAndValue(key, 0.0));
				}

			}
		} else {

			SocioEmotionalTestDataWrapper s = new SocioEmotionalTestDataWrapper();
			s.setOptions(socioEmotionalTestQuestionOptionRepository.findAll());
			s.setQuestions(socioEmotionalTestQuestionRepository.findBySocioEmotionalTestQuestionTypeId(socio_emotional_test_question_type_id));

			for (String key : testResultMap.keySet()) {

				List<SocioEmotionalTestResult> values = testResultMap.get(key);

				if (!values.isEmpty()) {

					double score = 0;

					for (SocioEmotionalTestResult test : values) {

						int total_score = 0;

						List<SocioEmotionalTestAnswer> socioEmotionalTestAnswers = socioEmotionalTestAnswerRepository
								.findBySocioEmotionalTestResultId(test.getSocio_emotional_test_result_id());

						for (int q = 0; q < s.getQuestions().size(); q++) {

							SocioEmotionalTestQuestion socioEmotionalTestQuestion = s.getQuestions().get(q);

							for (int a = 0; a < socioEmotionalTestAnswers.size(); a++) {
								if (socioEmotionalTestQuestion
										.getSocio_emotional_test_question_id() == socioEmotionalTestAnswers.get(a)
												.getSocio_emotional_test_question_id()) {

									SocioEmotionalTestAnswer socioEmotionalTestAnswer = socioEmotionalTestAnswers
											.get(a);

									for (int o = 0; o < s.getOptions().size(); o++) {
										if (socioEmotionalTestAnswer.getSocio_emotional_test_question_option_id() == s
												.getOptions().get(o).getSocio_emotional_test_question_option_id()) {

											SocioEmotionalTestQuestionOption socioEmotionalTestQuestionOption = s
													.getOptions().get(o);

											if (socioEmotionalTestQuestion.isReversed()) {
												total_score += socioEmotionalTestQuestionOption.getReverse_weight();
												
												System.out.println("score = " + socioEmotionalTestQuestionOption.getReverse_weight() + " total_score = " + total_score);
											} else {
												total_score += socioEmotionalTestQuestionOption.getWeight();
												
												System.out.println("score = " + socioEmotionalTestQuestionOption.getWeight() + " total_score = " + total_score);
											}
											
											break;
										}
									}
								}

							}

						}

						score = score + total_score;
						System.out.println("test id " + test.getSocio_emotional_test_result_id() + "  score = " + score);
					}

					double avgScore = score / values.size();

					System.out.println("Key"+ key + "avgScore  " + avgScore + "  score = " + score + " size (count) = " + values.size() );
					
					labelAndValues.add(new LabelAndValue(key, DigitUtils.formatDoubleInTwoDigit(avgScore)));

				} else {
					labelAndValues.add(new LabelAndValue(key, DigitUtils.formatDoubleInTwoDigit(0.0)));
				}

			}

		}

		return labelAndValues;
	}
	
	@Override
	public LinkedHashMap<String, List<LabelAndValue>> getSingleDemographicComparisonGraphData(GetDataDashBoardRequest request) {

		
		LinkedHashMap<String, List<LabelAndValue>> graphData = new LinkedHashMap<String, List<LabelAndValue>>();

		if (AppUtils.isNotNullOrEmpty(request.getGenders())) {

			for (String gender : request.getGenders()) {

				GetDataDashBoardRequest getDataDashBoardRequest = getGetDataDashBoardRequestWithReqeuest(
						request);
				
				getDataDashBoardRequest.getGenders().add(gender);

				graphData.put(gender, getSELStrandsGraphData(getDataDashBoardRequest));
			}
		} else if (AppUtils.isNotNullOrEmpty(request.getAges())) {
			for (Integer age : request.getAges()) {

				GetDataDashBoardRequest getDataDashBoardRequest = getGetDataDashBoardRequestWithReqeuest(
						request);
				
				getDataDashBoardRequest.getAges().add(age);

				graphData.put(String.valueOf(age), getSELStrandsGraphData(getDataDashBoardRequest));
			}
		} else if (AppUtils.isNotNullOrEmpty(request.getEthnicity())) {

			for (String ethnicity : request.getEthnicity()) {

				GetDataDashBoardRequest getDataDashBoardRequest = getGetDataDashBoardRequestWithReqeuest(
						request);
				
				getDataDashBoardRequest.getEthnicity().add(ethnicity);

				graphData.put(ethnicity, getSELStrandsGraphData(getDataDashBoardRequest));
			}
		} else if (AppUtils.isNotNullOrEmpty(request.getGradeIds())) {
			for (Long grade_id : request.getGradeIds()) {
				
				GetDataDashBoardRequest getDataDashBoardRequest = getGetDataDashBoardRequestWithReqeuest(
						request);

				getDataDashBoardRequest.getGradeIds().add(grade_id);

				graphData.put(gradeRepository.getOne(grade_id).getGrade_name(),
						getSELStrandsGraphData(getDataDashBoardRequest));
			}
		} else if (AppUtils.isNotNullOrEmpty(request.getClassIds())) {
			for (Long class_id : request.getClassIds()) {

				GetDataDashBoardRequest getDataDashBoardRequest = getGetDataDashBoardRequestWithReqeuest(
						request);
				
				getDataDashBoardRequest.getClassIds().add(class_id);

				graphData.put(classRepository.getOne(class_id).getClass_name(),
						getSELStrandsGraphData(getDataDashBoardRequest));
			}
		}

		return graphData;
	}

	@Override
	public LinkedHashMap<String, List<LabelAndValue>> getDemographicComparisonGraphData(GetDataDashBoardRequest request) {
		
		LinkedHashMap<String, List<LabelAndValue>> graphData = new LinkedHashMap<String, List<LabelAndValue>>();

		if (AppUtils.isNotNullOrEmpty(request.getGenders())) {
			
			GetDataDashBoardRequest getDataDashBoardRequest = getGetDataDashBoardRequestWithReqeuest(
					request);

			getDataDashBoardRequest.getGenders().addAll(request.getGenders());

			graphData.put("Gender", getSELStrandsGraphData(getDataDashBoardRequest));

		}

		if (AppUtils.isNotNullOrEmpty(request.getAges())) {

			GetDataDashBoardRequest getDataDashBoardRequest = getGetDataDashBoardRequestWithReqeuest(
					request);
			
			getDataDashBoardRequest.getAges().addAll(request.getAges());

			graphData.put("Age", getSELStrandsGraphData(getDataDashBoardRequest));

		}

		if (AppUtils.isNotNullOrEmpty(request.getEthnicity())) {

			GetDataDashBoardRequest getDataDashBoardRequest = getGetDataDashBoardRequestWithReqeuest(
					request);
			
			getDataDashBoardRequest.getEthnicity().addAll(request.getEthnicity());

			graphData.put("Ethnicity", getSELStrandsGraphData(getDataDashBoardRequest));

		}

		if (AppUtils.isNotNullOrEmpty(request.getGradeIds())) {
			
			GetDataDashBoardRequest getSocioEmotionalDataAnalyticsRequest = getGetDataDashBoardRequestWithReqeuest(
					request);

			getSocioEmotionalDataAnalyticsRequest.getGradeIds().addAll(request.getGradeIds());

			graphData.put("Grade", getSELStrandsGraphData(getSocioEmotionalDataAnalyticsRequest));

		}

		if (AppUtils.isNotNullOrEmpty(request.getClassIds())) {

			GetDataDashBoardRequest getDataDashBoardRequest = getGetDataDashBoardRequestWithReqeuest(
					request);
			
			getDataDashBoardRequest.getClassIds().addAll(request.getClassIds());

			graphData.put("Classroom", getSELStrandsGraphData(getDataDashBoardRequest));

		}

		return graphData;
	}
	
	@Override
	public List<LabelAndValueScore> getTimeSpentAndSELStrandsGraphData(GetDataDashBoardRequest request) {

		List<Long> student_ids = studentService.getStudentByDemographics(request);
	
		List<Activity> activityList = activityService.getTimeSpentByStudentIds(student_ids, request.getTimeLine());
		LinkedHashMap<String, List<Activity>> activityMap = activityService.getActivityMapByTimeLine(request.getTimeLine(), activityList);
		
		List<LabelAndValue> labelAndValueList = getSELStrandsGraphData(request);
		
		List<LabelAndValueScore> labelAndValueScoreList = new ArrayList<>();
		int tempInt = 100;
		for (String key : activityMap.keySet()) {
			
			List<Activity> values = activityMap.get(key);

			if (!values.isEmpty()) {

				double appUsedTime = 0;

				for (Activity activity : values) {
					 //milliseconds
			        double differnce = (activity.getEnd_date_time().getTime() - activity.getStart_date_time().getTime())/1000;
			        if(differnce >= 1) {
			        	appUsedTime = (appUsedTime + differnce);
			        }
				}
				
				/*for (int k= 0 ;k<10;k++) {
					 //milliseconds
			        double differnce = 6000 + tempInt;//(activity.getEnd_date_time().getTime() - activity.getStart_date_time().getTime())/1000;
			        if(differnce >= 1) {
			        	appUsedTime = (appUsedTime + differnce);
			        }
				}*/
						
				if(appUsedTime >= 1 && AppUtils.isNotNullOrEmpty(student_ids)) {
					System.out.println("key "+ key + " App Used Time in seconds " + appUsedTime + " in minutes " + ((double) appUsedTime / 60.0) + " Student Size " + student_ids.size() + " AVG Time " + (((double) appUsedTime / 60.0)/ student_ids.size()));
					labelAndValueScoreList.add(new LabelAndValueScore(key, DigitUtils.formatDoubleInTwoDigit(DigitUtils.getAverage(DigitUtils.getAverage((double) appUsedTime, 60.0), (double) student_ids.size())), 0.00));
				} else {
					labelAndValueScoreList.add(new LabelAndValueScore(key, DigitUtils.formatDoubleInTwoDigit(0.00), 0.00));
				}
			} else {
				labelAndValueScoreList.add(new LabelAndValueScore(key, DigitUtils.formatDoubleInTwoDigit(0.00), 0.00));
			}
			tempInt = tempInt + 100;
		}
		
		for(int i = 0; i <labelAndValueScoreList.size(); i++) {
			
			LabelAndValueScore labelAndValueScore = labelAndValueScoreList.get(i);
			
			for(int j = 0; j <labelAndValueList.size(); j++) {
				
				LabelAndValue labelAndValue = labelAndValueList.get(j);

				if(labelAndValueScore.getxLabel().equals(labelAndValue.getxLabel())) {
					labelAndValueScore.setScore( DigitUtils.formatDoubleInTwoDigit(labelAndValue.getValue()));
				}
			}
		}
		
		
		return labelAndValueScoreList;
	}
	
	private List<SocioEmotionalTestResult> getSocioEmotionalTestResultByStudentIds(List<Long> student_ids,
			TimeLine timeLine) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<SocioEmotionalTestResult> query = criteriaBuilder.createQuery(SocioEmotionalTestResult.class);
		Root<SocioEmotionalTestResult> root = query.from(SocioEmotionalTestResult.class);

		List<Predicate> predicates = new ArrayList<>();

		if (AppUtils.isNotNullOrEmpty(student_ids)) {
			Predicate genderPredicate = criteriaBuilder.in(root.get(SocioEmotionalTestResult_.STUDENT_ID)).value(student_ids);
			predicates.add(genderPredicate);
		}

		Date toDate = DateUtils.getToDateByTimeLine(timeLine);

		System.out.println(timeLine.toString() + " - ToDate " + DateUtils.dailyDateFormat.format(toDate) + " - FromDate "
				+ DateUtils.dailyDateFormat.format(new Date()));

		predicates.add(criteriaBuilder.greaterThan(root.<Date>get(SocioEmotionalTestResult_.CREATEDAT), toDate));

		query.where(predicates.toArray(new Predicate[0]));

		TypedQuery<SocioEmotionalTestResult> typedQuery = entityManager.createQuery(query);
		List<SocioEmotionalTestResult> socioEmotionalTestResultList = typedQuery.getResultList();
		System.out.println("SocioEmotionalTestResult Count = " + socioEmotionalTestResultList.size());
		return socioEmotionalTestResultList;
	}
	
	private LinkedHashMap<String, List<SocioEmotionalTestResult>> getSocioEmotionalTestResultMapByTimeLine(
			TimeLine timeLine, List<SocioEmotionalTestResult> socioEmotionalTestResultList) {

		LinkedHashMap<String, List<SocioEmotionalTestResult>> testResultMap = null;

		switch (timeLine) {
		case DAILY: {
			testResultMap = fillSocioEmotionalTestResultMap(DateUtils.getDailyLabels());
			for (SocioEmotionalTestResult testResult : socioEmotionalTestResultList) {
				String key = DateUtils.dailyDateFormat.format(testResult.getCreatedAt());
				AppUtils.addValueTotMap(testResultMap, key, testResult);
			}
			break;
		}
		case WEEKLY: {
			testResultMap = fillSocioEmotionalTestResultMap(DateUtils.getWeeklyLabels());
			for (SocioEmotionalTestResult testResult : socioEmotionalTestResultList) {
				String key = DateUtils.getWeekendByDate(testResult.getCreatedAt());
				AppUtils.addValueTotMap(testResultMap, key, testResult);
			}
			break;
		}
		case MONTHLY: {
			testResultMap = fillSocioEmotionalTestResultMap(DateUtils.getMonthlyLabels());
			for (SocioEmotionalTestResult testResult : socioEmotionalTestResultList) {
				String key = DateUtils.monthlyDateFormat.format(testResult.getCreatedAt());
				AppUtils.addValueTotMap(testResultMap, key, testResult);
			}
			break;
		}
		case YEARLY: {
			testResultMap = fillSocioEmotionalTestResultMap(DateUtils.getYearlyLabels());
			for (SocioEmotionalTestResult testResult : socioEmotionalTestResultList) {
				String key = DateUtils.yearlyDateFormat.format(testResult.getCreatedAt());
				AppUtils.addValueTotMap(testResultMap, key, testResult);
			}
			break;
		}
		}

		return testResultMap;
	}

	private LinkedHashMap<String, List<SocioEmotionalTestResult>> fillSocioEmotionalTestResultMap(List<String> keys) {
		
		LinkedHashMap<String, List<SocioEmotionalTestResult>> map = new LinkedHashMap<String, List<SocioEmotionalTestResult>>();

		for (String key : keys) {
			map.put(key, new ArrayList<SocioEmotionalTestResult>());
		}

		return map;
	}
	
	private GetDataDashBoardRequest getGetDataDashBoardRequestWithReqeuest(
			GetDataDashBoardRequest request) {
		GetDataDashBoardRequest getDataDashBoardRequest = new GetDataDashBoardRequest();
		getDataDashBoardRequest.setTimeLine(request.getTimeLine());
		getDataDashBoardRequest
				.setSocio_emotional_test_question_type_id(request.getSocio_emotional_test_question_type_id());
		getDataDashBoardRequest.setGraphType(request.getGraphType());
		getDataDashBoardRequest.setFromDate(request.getFromDate());
		getDataDashBoardRequest.setToDate(request.getToDate());
		return getDataDashBoardRequest;
	}
}
