/**
 * 
 */
package de.bstly.board.controller;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.google.common.collect.Lists;

import de.bstly.board.businesslogic.CommentManager;
import de.bstly.board.businesslogic.SettingsManager;
import de.bstly.board.businesslogic.VoteManager;
import de.bstly.board.controller.support.EntityResponseStatusException;
import de.bstly.board.controller.support.RequestBodyErrors;
import de.bstly.board.controller.validation.CommentValidator;
import de.bstly.board.model.Comment;
import de.bstly.board.model.Vote;
import de.bstly.board.model.support.FlaggedStatus;
import de.bstly.board.model.support.Types;
import de.bstly.board.model.support.VoteType;

/**
 * The Class CommentController.
 */
@RestController
@RequestMapping("/comments")
public class CommentController extends BaseController {

	@Autowired
	private CommentManager commentManager;
	@Autowired
	private CommentValidator commentValidator;
	@Autowired
	private VoteManager voteManager;
	@Autowired
	private SettingsManager settingsManager;

	/**
	 * Fetch by date.
	 *
	 * @param target          the target
	 * @param parent          the parent
	 * @param pageParameter   the page parameter
	 * @param sizeParameter   the size parameter
	 * @param dateParameter   the date parameter
	 * @param descParameter   the desc parameter
	 * @param ignoreParameter the ignore parameter
	 * @return the page
	 */
	@PreAuthorize("isAuthenticated()")
	@GetMapping({ "/new/{target}", "/new/{target}/{parent}" })
	public Page<Comment> fetchByDate(@PathVariable("target") Long target,
			@PathVariable("parent") Optional<Long> parent,
			@RequestParam("page") Optional<Integer> pageParameter,
			@RequestParam("size") Optional<Integer> sizeParameter,
			@RequestParam("date") Optional<Instant> dateParameter,
			@RequestParam("desc") Optional<Boolean> descParameter,
			@RequestParam("ignore") Optional<List<String>> ignoreParameter) {

		if (sizeParameter.isPresent() && sizeParameter.get() > 100) {
			sizeParameter = Optional.of(100);
		}

		if (dateParameter.isPresent() && dateParameter.get().isAfter(Instant.now())) {
			dateParameter = Optional.of(Instant.now());
		}
		Page<Comment> comments = commentManager.fetchByDate(getCurrentUsername(), target,
				parent.orElse(null), dateParameter.orElse(Instant.now()), pageParameter.orElse(0),
				sizeParameter.orElse(settingsManager.getPageSize()), descParameter.orElse(false));

		List<String> ignore = ignoreParameter.orElse(Lists.newArrayList("entry"));
		commentManager.applyMetadata(getCurrentUsername(), comments.getContent(), ignore);
		return comments;
	}

	/**
	 * Fetch by username.
	 *
	 * @param username        the username
	 * @param pageParameter   the page parameter
	 * @param sizeParameter   the size parameter
	 * @param dateParameter   the date parameter
	 * @param ascParameter    the asc parameter
	 * @param ignoreParameter the ignore parameter
	 * @return the page
	 */
	@PreAuthorize("isAuthenticated()")
	@GetMapping({ "/byuser/{username}" })
	public Page<Comment> fetchByUsername(@PathVariable("username") String username,
			@RequestParam("page") Optional<Integer> pageParameter,
			@RequestParam("size") Optional<Integer> sizeParameter,
			@RequestParam("date") Optional<Instant> dateParameter,
			@RequestParam("asc") Optional<Boolean> ascParameter,
			@RequestParam("ignore") Optional<List<String>> ignoreParameter) {

		if (sizeParameter.isPresent() && sizeParameter.get() > 100) {
			sizeParameter = Optional.of(100);
		}

		if (dateParameter.isPresent() && dateParameter.get().isAfter(Instant.now())) {
			dateParameter = Optional.of(Instant.now());
		}

		Page<Comment> comments = commentManager.fetchByUsername(username,
				dateParameter.orElse(Instant.now()), pageParameter.orElse(0),
				sizeParameter.orElse(settingsManager.getPageSize()), ascParameter.orElse(false));
		List<String> ignore = ignoreParameter.orElse(Lists.newArrayList());
		commentManager.applyMetadata(getCurrentUsername(), comments.getContent(), ignore);
		return comments;
	}

	/**
	 * Count comments.
	 *
	 * @param target the target
	 * @param parent the parent
	 * @return the long
	 */
	@PreAuthorize("isAuthenticated()")
	@GetMapping({ "/count/{target}", "/count/{target}/{parent}" })
	public Long countComments(@PathVariable("target") Long target,
			@PathVariable("parent") Optional<Long> parent) {
		return commentManager.count(target, parent.orElse(null));
	}

	/**
	 * Gets the comment.
	 *
	 * @param id              the id
	 * @param ignoreParameter the ignore parameter
	 * @return the comment
	 */
	@PreAuthorize("isAuthenticated()")
	@GetMapping("/comment/{id}")
	public Comment getComment(@PathVariable("id") Long id,
			@RequestParam("ignore") Optional<List<String>> ignoreParameter) {
		Comment comment = commentManager.get(id);

		if (comment == null) {
			throw new EntityResponseStatusException(HttpStatus.NOT_FOUND);
		}

		List<String> ignore = ignoreParameter.orElse(Lists.newArrayList());
		commentManager.applyMetadata(getCurrentUsername(), comment, ignore);
		return comment;
	}

	/**
	 * Creates the comment.
	 *
	 * @param comment         the comment
	 * @param ignoreParameter the ignore parameter
	 * @return the comment
	 */
	@PreAuthorize("isAuthenticated()")
	@PostMapping
	public Comment createComment(@RequestBody Comment comment,
			@RequestParam("ignore") Optional<List<String>> ignoreParameter) {
		RequestBodyErrors bindingResult = new RequestBodyErrors(comment);
		commentValidator.validate(comment, bindingResult);

		if (bindingResult.hasErrors()) {
			throw new EntityResponseStatusException(bindingResult.getAllErrors(),
					HttpStatus.UNPROCESSABLE_ENTITY);
		}

		comment.setCreated(Instant.now().plus(getCommentDelay(), ChronoUnit.MINUTES));
		comment.setAuthor(getCurrentUsername());
		comment.setText(comment.getText().trim());
		comment.setFlaggedStatus(FlaggedStatus.NORMAL);
		comment = commentManager.save(comment);

		Vote vote = new Vote();
		vote.setTarget(comment.getId());
		vote.setTargetType(Types.comment);
		vote.setType(VoteType.up);
		vote.setAuthor(getCurrentUsername());
		voteManager.save(vote);

		List<String> ignore = ignoreParameter.orElse(Lists.newArrayList("entry"));
		commentManager.applyMetadata(getCurrentUsername(), comment, ignore);

		return comment;
	}

	/**
	 * Update comment.
	 *
	 * @param comment         the comment
	 * @param ignoreParameter the ignore parameter
	 * @return the comment
	 */
	@PreAuthorize("isAuthenticated()")
	@PatchMapping
	public Comment updateComment(@RequestBody Comment comment,
			@RequestParam("ignore") Optional<List<String>> ignoreParameter) {
		Comment orgComment = commentManager.get(comment.getId());
		if (orgComment == null || !orgComment.getAuthor().equals(getCurrentUsername())
				|| orgComment.getCreated().isBefore(Instant.now())) {
			throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
		}

		RequestBodyErrors bindingResult = new RequestBodyErrors(comment);
		commentValidator.validate(comment, bindingResult);

		if (bindingResult.hasErrors()) {
			throw new EntityResponseStatusException(bindingResult.getAllErrors(),
					HttpStatus.UNPROCESSABLE_ENTITY);
		}

		orgComment.setText(comment.getText());
		orgComment = commentManager.save(orgComment);
		List<String> ignore = ignoreParameter.orElse(Lists.newArrayList("entry"));
		commentManager.applyMetadata(getCurrentUsername(), orgComment, ignore);
		return comment;
	}

	/**
	 * Detele comment.
	 *
	 * @param id the id
	 */
	@PreAuthorize("isAuthenticated()")
	@DeleteMapping("{id}")
	public void deteleComment(@PathVariable("id") Long id) {
		Comment orgComment = commentManager.get(id);
		if (orgComment == null || !orgComment.getAuthor().equals(getCurrentUsername())
				|| orgComment.getCreated().isBefore(Instant.now())) {
			throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
		}

		commentManager.delete(orgComment);
	}

}
