package com.infinite.focus.server.activity.service;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;

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.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.activity.Activity;
import com.infinite.focus.server.activity.ActivityRepository;
import com.infinite.focus.server.activity.Activity_;
import com.infinite.focus.server.auth.Instructor;
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.dashboard.GetDataDashBoardRequest;
import com.infinite.focus.server.dashboard.TimeLine;
import com.infinite.focus.server.grade.GradeRepository;
import com.infinite.focus.server.instructor.Instructor_;
import com.infinite.focus.server.standard.StandardRepository;
import com.infinite.focus.server.students.ClassRepository;
import com.infinite.focus.server.students.Student_;
import com.infinite.focus.server.utils.AppUtils;
import com.infinite.focus.server.utils.DateUtils;
import com.infinite.focus.server.utils.UnitExpression;

@Service
public class ActivityServiceImpl implements ActivityService {

	@Autowired
	EntityManager entityManager;

	@Autowired
	GradeRepository gradeRepository;

	@Autowired
	StandardRepository standardRepository;

	@Autowired
	StudentRepository studentRepository;

	@Autowired
	InstructorRepository instructorRepository;

	@Autowired
	ClassRepository classRepository;

	@Autowired
	ActivityRepository activityRepository;
	
	String forwardSlash = "/";

	@Override
	public List<Activity> getAllStudentActivitiesByActivityType(GetDataDashBoardRequest request,
			Long activity_type_id) {

		TimeLine timeLine = request.getTimeLine();

		CriteriaBuilder qb = entityManager.getCriteriaBuilder();
		CriteriaQuery<Activity> query = qb.createQuery(Activity.class);
		Root<Activity> root = query.from(Activity.class);
		Join<Activity, Student> s = root.join("student", JoinType.LEFT);

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

		if (AppUtils.isNotNullOrEmpty(request.getGenders())) {
			Predicate genderPredicate = qb.in(s.get(Student_.GENDER)).value(request.getGenders());
			predicates.add(genderPredicate);
		}
		if (AppUtils.isNotNullOrEmpty(request.getAges())) {
			Expression<String> year = new UnitExpression(null, String.class, "YEAR");
		    Expression<Integer> timeInYears = qb.function(
		        "TIMESTAMPDIFF",
		        Integer.class,
		        year ,
		        s.<Timestamp>get(Student_.DATE_OF_BIRTH),
		        qb.literal(Timestamp.valueOf(LocalDateTime.now())));
			In<Integer> inClause = qb.in(timeInYears);
			for (Integer age : request.getAges()) {
			    inClause.value(age);
			}
			//Predicate agePredicate = qb.in(s.get(Student_.AGE)).value(request.getAges());
			predicates.add(inClause);
		}
		if (AppUtils.isNotNullOrEmpty(request.getEthnicity())) {
			Predicate ethnicityPredicate = qb.in(s.get(Student_.ETHNICITY)).value(request.getEthnicity());
			predicates.add(ethnicityPredicate);
		}
		if (AppUtils.isNotNullOrEmpty(request.getGradeIds())) {
			Predicate gradePredicate = qb.in(s.get(Student_.GRADE_ID)).value(request.getGradeIds());
			predicates.add(gradePredicate);
		}

		if (AppUtils.isNotNullOrEmpty(request.getClassIds())) {
			Predicate classesPredicate = qb.in(s.get(Student_.CLASS_ID)).value(request.getClassIds());
			predicates.add(classesPredicate);
		}

		predicates.add(qb.notEqual(s.get(Student_.STUDENT_ID), 0L));
		predicates.add(qb.equal(root.get(Activity_.ACTIVITY_TYPE_ID), activity_type_id));

		if (timeLine != null) {
			Date toDate = DateUtils.getToDateByTimeLine(timeLine);

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

			predicates.add(qb.greaterThan(root.<Date>get(Activity_.START_DATE_TIME), toDate));
		} else if(request.getFromDate() != null && request.getToDate() != null) {
			System.out.println("getAllStudentActivitiesByActivityType FromDate " + DateUtils.dayWitTimeFormat.format(request.getFromDate()) + " - ToDate " + DateUtils.dayWitTimeFormat.format(request.getToDate()));

			predicates.add(qb.between(root.<Date>get(Activity_.START_DATE_TIME), request.getFromDate(), request.getToDate()));
		}

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

		TypedQuery<Activity> typedQuery = entityManager.createQuery(query);
		List<Activity> activityList = typedQuery.getResultList();
//		if (AppUtils.isNotNullOrEmpty(request.getAges())) {
//			List<Activity> activityList_with_age = new ArrayList<>();
//			for(Activity activity : activityList) {
//				if(activity.getStudent_id()!=null) {
//					Student student = studentRepository.getOne(activity.getStudent_id());
//					if(request.getAges().contains(DateUtils.getAge(student.getDate_of_birth()))) {
//						activityList_with_age.add(activity);
//					}
//				}
//				
//			}
//			System.out.println("Activity Count = " + activityList_with_age.size());
//			return activityList_with_age;
//		}
		System.out.println("Activity Count = " + activityList.size());
		return activityList;
	}
	
	@Override
	public List<Activity> getAllInstructorActivitiesByActivityType(GetDataDashBoardRequest request, Long activity_type_id) {
		
		TimeLine timeLine = request.getTimeLine();
		
		CriteriaBuilder qb = entityManager.getCriteriaBuilder();
		CriteriaQuery<Activity> query = qb.createQuery(Activity.class);
		Root<Activity> root = query.from(Activity.class);
		Join<Activity, Instructor> i = root.join("instructor", JoinType.LEFT);

		List<Predicate> predicates = new ArrayList<>();
		
		if (AppUtils.isNotNullOrEmpty(request.getGradeIds())) {
			Predicate gradePredicate = qb.in(i.get(Instructor_.GRADE_ID)).value(request.getGradeIds());
			predicates.add(gradePredicate);
		}
		
		predicates.add(qb.notEqual(i.get(Instructor_.INSTRUCTOR_ID), 0L));
		predicates.add(qb.equal(root.get(Activity_.ACTIVITY_TYPE_ID), activity_type_id));
		
		if (timeLine != null) {
			Date toDate = DateUtils.getToDateByTimeLine(timeLine);

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

			predicates.add(qb.greaterThan(root.<Date>get(Activity_.START_DATE_TIME), toDate));
		} else if(request.getFromDate() != null && request.getToDate() != null) {
			System.out.println("getAllInstructorActivitiesByActivityType FromDate " + DateUtils.dailyDateFormat.format(request.getFromDate()) + " - ToDate " + DateUtils.dailyDateFormat.format(request.getToDate()));

			predicates.add(qb.between(root.<Date>get(Activity_.START_DATE_TIME), request.getFromDate(), request.getToDate()));
		}
		
		query.where(predicates.toArray(new Predicate[0]));

		TypedQuery<Activity> typedQuery = entityManager.createQuery(query);
		List<Activity> activityList = typedQuery.getResultList();
		System.out.println("Activity Count = " + activityList.size());
		return activityList;
	}
	
	@Override
	public List<Long> getAllStudentActivitiesByActivityTypeIdAndEnitityId(GetDataDashBoardRequest request, Long activity_type_id, Long enitity_id) {
		
		CriteriaBuilder qb = entityManager.getCriteriaBuilder();
		CriteriaQuery<Long> query = qb.createQuery(Long.class);
		Root<Activity> root = query.from(Activity.class);
		Join<Activity, Student> s = root.join("student", JoinType.LEFT);
		
		List<Predicate> predicates = new ArrayList<>();
		
		if (AppUtils.isNotNullOrEmpty(request.getGenders())) {
			Predicate genderPredicate = qb.in(s.get(Student_.GENDER)).value(request.getGenders());
			predicates.add(genderPredicate);
		}
		if (AppUtils.isNotNullOrEmpty(request.getAges())) {
			Expression<String> year = new UnitExpression(null, String.class, "YEAR");
		    Expression<Integer> timeInYears = qb.function(
		        "TIMESTAMPDIFF",
		        Integer.class,
		        year ,
		        s.<Timestamp>get(Student_.DATE_OF_BIRTH),
		        qb.literal(Timestamp.valueOf(LocalDateTime.now())));
			In<Integer> inClause = qb.in(timeInYears);
			for (Integer age : request.getAges()) {
			    inClause.value(age);
			}
			//Predicate agePredicate = qb.in(s.get(Student_.AGE)).value(request.getAges());
			predicates.add(inClause);
		}
		if (AppUtils.isNotNullOrEmpty(request.getEthnicity())) {
			Predicate ethnicityPredicate = qb.in(s.get(Student_.ETHNICITY)).value(request.getEthnicity());
			predicates.add(ethnicityPredicate);
		}
		if (AppUtils.isNotNullOrEmpty(request.getGradeIds())) {
			Predicate gradePredicate = qb.in(s.get(Student_.GRADE_ID)).value(request.getGradeIds());
			predicates.add(gradePredicate);
		}
		if (AppUtils.isNotNullOrEmpty(request.getClassIds())) {
			Predicate classesPredicate = qb.in(s.get(Student_.CLASS_ID)).value(request.getClassIds());
			predicates.add(classesPredicate);
		}
		
		predicates.add(qb.notEqual(s.get(Student_.STUDENT_ID), 0L));
		predicates.add(qb.equal(root.get(Activity_.ACTIVITY_TYPE_ID), activity_type_id));
		predicates.add(qb.equal(root.get(Activity_.ENTITY_ID), enitity_id));
		

		query.select(root.get(Activity_.ACTIVITY_ID)).where(predicates.toArray(new Predicate[0]));

		TypedQuery<Long> typedQuery = entityManager.createQuery(query);
		List<Long> activity_ids = typedQuery.getResultList();
//		if (AppUtils.isNotNullOrEmpty(request.getAges())) {
//			List<Long> activity_ids_with_age = new ArrayList<>();
//			for(Activity activity : activityRepository.findByActivityIds(activity_ids)) {
//				if(activity.getStudent_id() != null) {
//					Student student = studentRepository.getOne(activity.getStudent_id());
//					if(request.getAges().contains(DateUtils.getAge(student.getDate_of_birth()))) {
//						activity_ids_with_age.add(activity.getActivity_id());
//					}
//				}
//				
//			}
//			System.out.println("Activity Count = " + activity_ids_with_age.size());
//			return activity_ids_with_age;
//		}
		System.out.println("Activity Count = " + activity_ids.size());
		return activity_ids;
	}	
	
	@Override
	public List<Activity> getTimeSpentByStudentIds(List<Long> student_ids, TimeLine timeLine) {
		
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Activity> query = criteriaBuilder.createQuery(Activity.class);
		Root<Activity> root = query.from(Activity.class);
		Join<Activity, Student> s = root.join("student", JoinType.LEFT);

		List<Predicate> predicates = new ArrayList<>();
		
		predicates.add(criteriaBuilder.equal(root.get(Activity_.ACTIVITY_TYPE_ID), 1));

		if (AppUtils.isNotNullOrEmpty(student_ids)) {
			Predicate genderPredicate = criteriaBuilder.in(s.get(Activity_.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(Activity_.START_DATE_TIME), toDate));
		
		query.where(predicates.toArray(new Predicate[0]));

		TypedQuery<Activity> typedQuery = entityManager.createQuery(query);
		List<Activity> activityList = typedQuery.getResultList();
		System.out.println("Activity Count = " + activityList.size());
		return activityList;
	}
	
	@Override
	public LinkedHashMap<String, List<Activity>> getActivityMapByTimeLine(
			TimeLine timeLine, List<Activity> activityList) {

		LinkedHashMap<String, List<Activity>> activityMap = null;

		switch (timeLine) {
		case DAILY: {
			activityMap = fillActivityMap(DateUtils.getDailyLabels());
			for (Activity activity : activityList) {
				String key = DateUtils.dailyDateFormat.format(activity.getStart_date_time());
				AppUtils.addValueTotMap(activityMap, key, activity);
			}
			break;
		}
		case WEEKLY: {
			activityMap = fillActivityMap(DateUtils.getWeeklyLabels());
			for (Activity activity : activityList) {
				String key = DateUtils.getWeekendByDate(activity.getStart_date_time());
				AppUtils.addValueTotMap(activityMap, key, activity);
			}
			break;
		}
		case MONTHLY: {
			activityMap = fillActivityMap(DateUtils.getMonthlyLabels());
			for (Activity activity : activityList) {
				String key = DateUtils.monthlyDateFormat.format(activity.getStart_date_time());
				AppUtils.addValueTotMap(activityMap, key, activity);
			}
			break;
		}
		case YEARLY: {
			activityMap = fillActivityMap(DateUtils.getYearlyLabels());
			for (Activity activity : activityList) {
				String key = DateUtils.yearlyDateFormat.format(activity.getStart_date_time());
				AppUtils.addValueTotMap(activityMap, key, activity);
			}
			break;
		}
		}

		return activityMap;
	}

	private LinkedHashMap<String, List<Activity>> fillActivityMap(List<String> keys) {
		
		LinkedHashMap<String, List<Activity>> map = new LinkedHashMap<String, List<Activity>>();

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

		return map;
	}

	@Override
	public List<Activity> getMoodInsightByInstructerId(GetDataDashBoardRequest request)  {
		CriteriaBuilder qb = entityManager.getCriteriaBuilder();
		CriteriaQuery<Activity> query = qb.createQuery(Activity.class);
		Root<Activity> root = query.from(Activity.class);
		Join<Activity, Student> s = root.join("student", JoinType.LEFT);
		
		List<Predicate> predicates = new ArrayList<>();
		
		if (AppUtils.isNotNullOrEmpty(request.getGenders())) {
			Predicate genderPredicate = qb.in(s.get(Student_.GENDER)).value(request.getGenders());
			predicates.add(genderPredicate);
		}
		if (AppUtils.isNotNullOrEmpty(request.getAges())) {
			Expression<String> year = new UnitExpression(null, String.class, "YEAR");
		    Expression<Integer> timeInYears = qb.function(
		        "TIMESTAMPDIFF",
		        Integer.class,
		        year ,
		        s.<Timestamp>get(Student_.DATE_OF_BIRTH),
		        qb.literal(Timestamp.valueOf(LocalDateTime.now())));
			In<Integer> inClause = qb.in(timeInYears);
			for (Integer age : request.getAges()) {
			    inClause.value(age);
			}
			//Predicate agePredicate = qb.in(s.get(Student_.AGE)).value(request.getAges());
			predicates.add(inClause);
		}
		if (AppUtils.isNotNullOrEmpty(request.getEthnicity())) {
			Predicate ethnicityPredicate = qb.in(s.get(Student_.ETHNICITY)).value(request.getEthnicity());
			predicates.add(ethnicityPredicate);
		}
		if (AppUtils.isNotNullOrEmpty(request.getGradeIds())) {
			Predicate gradePredicate = qb.in(s.get(Student_.GRADE_ID)).value(request.getGradeIds());
			predicates.add(gradePredicate);
		}
		if (AppUtils.isNotNullOrEmpty(request.getClassIds())) {
			Predicate classesPredicate = qb.in(s.get(Student_.CLASS_ID)).value(request.getClassIds());
			predicates.add(classesPredicate);
		}
		
		if (AppUtils.isNotNullOrEmpty(request.getMood_update_before())) {
			//System.out.println(request.getMood_update_before().get(0));
			Predicate moodUpdateBeforePredicate = qb.in(root.get(Activity_.MOOD_UPDATE_BEFORE)).value(request.getMood_update_before());
			predicates.add(moodUpdateBeforePredicate);
		}
		
		if (AppUtils.isNotNullOrEmpty(request.getMood_update_after())) {
			//System.out.println(request.getMood_update_after().get(0));
			Predicate moodUpdateAfterPredicate = qb.in(root.get(Activity_.MOOD_UPDATE_AFTER)).value(request.getMood_update_after());
			predicates.add(moodUpdateAfterPredicate);
		}
		
		if(request.getFromDate() != null && request.getToDate() != null) {
			System.out.println("getMoodInsightByInstructerId FromDate " + DateUtils.dailyDateFormat.format(request.getFromDate()) + " - ToDate " + DateUtils.dailyDateFormat.format(request.getToDate()));

			predicates.add(qb.between(root.<Date>get(Activity_.START_DATE_TIME), request.getFromDate(), request.getToDate()));
		}
		
		
		predicates.add(qb.notEqual(s.get(Student_.STUDENT_ID), 0L));
		predicates.add(qb.equal(s.get(Student_.INSTRUCTOR_ID), request.getInstructor_id()));
		predicates.add(qb.equal(root.get(Activity_.ACTIVITY_TYPE_ID), 2));
//		predicates.add(qb.equal(root.get(Activity_.ENTITY_ID), enitity_id));
		

		query.where(predicates.toArray(new Predicate[0])).orderBy(qb.asc(root.<Date>get(Activity_.END_DATE_TIME)));

		TypedQuery<Activity> typedQuery = entityManager.createQuery(query);
		List<Activity> activities = typedQuery.getResultList();
//		if (AppUtils.isNotNullOrEmpty(request.getAges())) {
//			List<Long> activity_ids_with_age = new ArrayList<>();
//			for(Activity activity : activityRepository.findByActivityIds(activity_ids)) {
//				if(activity.getStudent_id() != null) {
//					Student student = studentRepository.getOne(activity.getStudent_id());
//					if(request.getAges().contains(DateUtils.getAge(student.getDate_of_birth()))) {
//						activity_ids_with_age.add(activity.getActivity_id());
//					}
//				}
//				
//			}
//			System.out.println("Activity Count = " + activity_ids_with_age.size());
//			return activity_ids_with_age;
//		}
//		System.out.println("Activity Count = " + activity_ids.size());
		return activities;
	}
}
