package org.ros.chatto.controller;

import java.security.Principal;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;

import javax.validation.Valid;

import org.ros.chatto.dto.ActiveUserDTO;
import org.ros.chatto.dto.ChatMessageDTO;
import org.ros.chatto.dto.MessageCipherDTO;
import org.ros.chatto.error.ErrorModel;
import org.ros.chatto.error.ErrorResponse;
import org.ros.chatto.service.ChatService;
import org.ros.chatto.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/chat")
@RequiredArgsConstructor
public class ChatMessageController {

	private final ChatService chatService;
	private final UserService userService;

	@PostMapping(value = "/post/message", consumes = { "application/json" })
	public ResponseEntity<?> newMessage(
			@RequestBody @Valid final ChatMessageDTO chatMessageDTO,
			final BindingResult bindingResult, final Principal principal) {
		if (bindingResult.hasErrors()) {
			return new ResponseEntity<ErrorResponse>(
					handleValidationErrors(bindingResult),
					HttpStatus.BAD_REQUEST);
		}
		final MessageCipherDTO messageCipher = chatMessageDTO
				.getMessageCipher();
		final String fromUser = principal.getName();
		final String toUser = chatMessageDTO.getToUser();
		System.out.println("Message cipher = " + messageCipher);
		final ChatMessageDTO chatMessageDTOSaved = chatService
				.createMessage(fromUser, toUser, messageCipher);
		return new ResponseEntity<ChatMessageDTO>(chatMessageDTOSaved,
				HttpStatus.CREATED);
	}

	private ErrorResponse handleValidationErrors(
			final BindingResult bindingResult) {

		final List<ErrorModel> errorMessages = bindingResult.getFieldErrors()
				.stream()
				.map(err -> new ErrorModel(err.getField(),
						err.getRejectedValue(), err.getDefaultMessage()))
				.distinct().collect(Collectors.toList());
		return ErrorResponse.builder().errorMessage(errorMessages).build();
	}

	@GetMapping(value = "/get/messages/{userName}")
	public List<ChatMessageDTO> getAllMessages(
			@PathVariable final String userName, final Principal principal) {
		final List<ChatMessageDTO> chatMessageDTOs = chatService
				.getAllMessages(principal.getName(), userName);
		return chatMessageDTOs;
	}

	@GetMapping(value = "/get/messages/{userName}", params = { "page", "size" })
	public List<ChatMessageDTO> findPaginated(
			@RequestParam("page") final int page,
			@RequestParam("size") final int size,
			@PathVariable final String userName, final Principal principal) {
		final List<ChatMessageDTO> chatMessageDTOs = chatService
				.getMessagePage(principal.getName(), userName, page, size);
		return chatMessageDTOs;
	}

	@GetMapping(value = "/get/messages/{userName}/{lastMessageTime}")
	public List<ChatMessageDTO> sendNewMessages(
			@PathVariable final String userName,
			@PathVariable final Instant lastMessageTime,
			final Principal principal) {
		final List<ChatMessageDTO> chatMessageDTOs = chatService.getNewMessages(
				principal.getName(), userName, lastMessageTime);
		return chatMessageDTOs;
	}

	@GetMapping("/get/users")
	public List<String> getAllOtherUsers(final Principal principal) {
		return userService.findAllOtherUsers(principal.getName());
	}

	@GetMapping("/get/active-users")
	public List<ActiveUserDTO> getAllOtherActiveUsers(
			final Principal principal) {
		return userService.getOtherActiveUsers(principal.getName());
	}

	@GetMapping("/get/token")
	public ResponseEntity<?> getToken() {
		return new ResponseEntity<String>(HttpStatus.OK);
	}
}
