package com.infinite.focus.server.resourcelibrary;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
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 javax.persistence.criteria.Subquery;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
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.lang.NonNull;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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 com.infinite.focus.server.activity.Activity;
import com.infinite.focus.server.auth.AuthenticationController;
import com.infinite.focus.server.auth.Message;
import com.infinite.focus.server.auth.SecurityConstants;
import com.infinite.focus.server.auth.Student;
import com.infinite.focus.server.categories.Category;
import com.infinite.focus.server.categories.CategoryRepository;
import com.infinite.focus.server.contents.Content;
import com.infinite.focus.server.contents.ContentRepository;
import com.infinite.focus.server.moods.Mood;
import com.infinite.focus.server.moods.MoodRepository;
import com.infinite.focus.server.students.Student_;
import com.infinite.focus.server.utils.AppUtils;
import com.infinite.focus.server.vimeo.VimeoService;
import com.infinite.focus.server.vimeo.VimeoVideoDetails;

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

public class PostController {

	@Autowired
	PostRepository postRepository;

	@Autowired
	CategoryRepository categoryRepository;

	@Autowired
	ContentRepository contentRepository;

	@Autowired
	MoodRepository moodRepository;

	@Autowired
	VimeoService vimeoService;
	
	@Autowired
	EntityManager entityManager;

	private static final Pattern VIMEO_PATTERN = Pattern
			.compile("[http|https]+:\\/\\/(?:www\\.|)vimeo\\.com\\/([a-zA-Z0-9_\\-]+)(&.+)?", Pattern.CASE_INSENSITIVE);

	public PostController() {

	}

	@PostMapping("/create/post")
	@ResponseBody
	public ResponseEntity<Post> createPost(@RequestHeader(SecurityConstants.HEADER_STRING) String token,
			@RequestParam(value = "post_title") String post_title,
			@RequestParam(value = "post_description") String post_description,
			@RequestParam(required = false, value = "post_cover_image_url") String post_cover_image_url,
			@RequestParam(required = false, value = "post_duration") String post_duration,
			@RequestParam(required = false, value = "post_video_url") String post_video_url,
			@RequestParam(value = "post_type") PostType post_type,
			@RequestParam(value = "categories") String categories,
			@RequestParam(value = "contents") String contents,
			@RequestParam(value = "moods") String moods) {

		AuthenticationController.isAuthenticated(token);

		Post requestPost = new Post();
		requestPost.setPost_title(post_title);
		requestPost.setPost_description(post_description);
		requestPost.setPost_cover_image_url(post_cover_image_url);
		requestPost.setPost_description(post_description);
		requestPost.setPost_video_url(post_video_url);
		requestPost.setPost_type(post_type);

		if (requestPost.getPost_type().equals(PostType.VIDEO)) {

			String url = post_video_url.trim();

			System.out.println("Vimeo Video Url - " + url);

			Long video_id = getVimeoVideoId(url);

			System.out.println("Vimeo Video Id - " + video_id);

			if (video_id != null) {
				try {
					VimeoVideoDetails vimeoVideoDetails = vimeoService.getVimeoVideoDetailsByVideoId(video_id);
					requestPost.setPost_cover_image_url(vimeoVideoDetails.getThumbnail_large());
					requestPost.setPost_duration(vimeoVideoDetails.getDuration());

				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

		Post post = postRepository.save(requestPost);

		List<String> categoryIds = Arrays.asList(categories.split("\\s*,\\s*"));

		for (String categoryId : categoryIds) {
			Category category = categoryRepository.findByCategoryId(Long.valueOf(categoryId));
			post.getCategories().add(category);
		}

		List<String> contentIds = Arrays.asList(contents.split("\\s*,\\s*"));

		for (String contentId : contentIds) {
			Content content = contentRepository.findByContentId(Long.valueOf(contentId));
			post.getContents().add(content);
		}

		List<String> moodIds = Arrays.asList(moods.split("\\s*,\\s*"));

		for (String moodId : moodIds) {
			Mood mood = moodRepository.findByMoodId(Long.valueOf(moodId));
			post.getMoods().add(mood);
		}

		return new ResponseEntity<Post>(postRepository.save(post), HttpStatus.OK);

	}

	public static long getVimeoVideoId(@NonNull String url) {
		Matcher vimeo = VIMEO_PATTERN.matcher(url);
		if (!vimeo.find())
			return -1;

		return Long.valueOf(vimeo.group(1));
	}
	
	@GetMapping("/get/posts")
	public ResponseEntity<Page<Post>> getPosts(@RequestHeader(SecurityConstants.HEADER_STRING) String token,
			@RequestParam(value = "post_title", defaultValue = "") String post_title,
			@RequestParam(value = "post_types", defaultValue = "") String post_types,
			@RequestParam(value = "categories", defaultValue = "") String categories,
			@RequestParam(value = "contents", defaultValue = "") String contents,
			@RequestParam(value = "moods", defaultValue = "") String moods,
			@RequestParam(value = "pageNo", defaultValue = "0") Integer pageNo,
			@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
			@RequestParam(value = "sortBy", defaultValue = "post_id") String sortBy,
			@RequestParam(value = "orderBy", defaultValue = "desc") String orderBy) {

		AuthenticationController.isAuthenticated(token);
		
		List<PostType> postTypes = new ArrayList<>();
		
		for (String post_type : Arrays.asList(post_types.split("\\s*,\\s*"))) {
			if(post_type.equals(PostType.ARTICLE.toString())){
				postTypes.add(PostType.ARTICLE);
			}
			if(post_type.equals(PostType.VIDEO.toString())){
				postTypes.add(PostType.VIDEO);
			}
		}
		
		CriteriaBuilder qb = entityManager.getCriteriaBuilder();
		CriteriaQuery<Post> query = qb.createQuery(Post.class);
		Root<Post> postRoot = query.from(Post.class);
		
		
				 
		List<Predicate> predicates = new ArrayList<Predicate>();
		

		if(AppUtils.isNotNullOrEmpty(post_title)) {
			 predicates.add(qb.like(qb.lower(postRoot.get("post_title")), 
	                    "%" + post_title.toLowerCase() + "%"));
		}
			
		if(AppUtils.isNotNullOrEmpty(postTypes)) {
			Predicate postTypePredicate = qb.in(postRoot.get("post_type")).value(postTypes);
			predicates.add(postTypePredicate);
		}
		
		List<Long> categoryIds = AppUtils.convertCommaSeparatedStringIdsToLongIds(categories);

		if(AppUtils.isNotNullOrEmpty(categoryIds)) {
			Subquery<Long> sq = query.subquery(Long.class).select(qb.literal(1L));
			Root<Post> sqRoot = sq.correlate(postRoot);
		    Join<Post, Category> sqJoin = sqRoot.join("categories");
		    sq.where(sqJoin.get("category_id").in(categoryIds));
		    predicates.add(qb.and(qb.exists(sq)));
		}
		
		List<Long> contentIds = AppUtils.convertCommaSeparatedStringIdsToLongIds(contents);

		if(AppUtils.isNotNullOrEmpty(contentIds)) {
			Subquery<Long> sq = query.subquery(Long.class).select(qb.literal(1L));
			Root<Post> sqRoot = sq.correlate(postRoot);
		    Join<Post, Content> sqJoin = sqRoot.join("contents");
		    sq.where(sqJoin.get("content_id").in(contentIds));
		    predicates.add(qb.and(qb.exists(sq)));
		}
		
		List<Long> moodIds = AppUtils.convertCommaSeparatedStringIdsToLongIds(moods);

		if(AppUtils.isNotNullOrEmpty(moodIds)) {
			Subquery<Long> sq = query.subquery(Long.class).select(qb.literal(1L));
			Root<Post> sqRoot = sq.correlate(postRoot);
		    Join<Post, Mood> sqJoin = sqRoot.join("moods");
		    sq.where(sqJoin.get("mood_id").in(moodIds));
		    predicates.add(qb.and(qb.exists(sq)));
		}
		
		query.where(qb.and(predicates.toArray( new Predicate[predicates.size()])));

		if(orderBy.equals("desc")) {
			query.orderBy(qb.desc(postRoot.get(sortBy)));
		} else {
			query.orderBy(qb.asc(postRoot.get(sortBy)));
		}

		Pageable pageable = PageRequest.of(pageNo, pageSize, orderBy.equals("desc") ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending());

		 // This query fetches the Books as per the Page Limit
        List<Post> result = entityManager.createQuery(query).setFirstResult((int) pageable.getOffset()).setMaxResults(pageable.getPageSize()).getResultList();

        // Create Count Query
        CriteriaQuery<Long> countQuery = qb.createQuery(Long.class);
        Root<Post> booksRootCount = countQuery.from(Post.class);
        countQuery.select(qb.count(booksRootCount)).where(qb.and(predicates.toArray(new Predicate[predicates.size()])));

        // Fetches the count of all Books as per given criteria
        Long count = entityManager.createQuery(countQuery).getSingleResult();

        Page<Post> pagedResult = new PageImpl<>(result, pageable, count);
    
		return new ResponseEntity<Page<Post>>(pagedResult, new HttpHeaders(), HttpStatus.OK);
	}
}