package org.ros.chatto.service;

import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.modelmapper.ModelMapper;
import org.ros.chatto.dto.ChatMessageDTO;
import org.ros.chatto.dto.MessageCipherDTO;
import org.ros.chatto.dto.ReencryptionDTO;
import org.ros.chatto.model.ChatMessage;
import org.ros.chatto.model.ChatUser;
import org.ros.chatto.model.MessageCipher;
import org.ros.chatto.repository.ChatMessageRepository;
import org.ros.chatto.repository.MessageCipherRepository;
import org.ros.chatto.repository.UserRepository;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;

@Service
@Transactional
@RequiredArgsConstructor
public class ChatServiceImpl implements ChatService {

	private final ModelMapper modelMapper;
	private final UserRepository userRepository;
	private final MessageCipherRepository messageCipherRepository;
	private final ChatMessageRepository chatMessageRepository;
	private final MyConversionService myConversionService;

	public ChatMessageDTO createMessage(final String fromUserName,
			final String toUserName, final MessageCipherDTO messageCipherDTO) {

		final Optional<ChatUser> fromUser = userRepository
				.findByUserName(fromUserName);
		final Optional<ChatUser> toUser = userRepository
				.findByUserName(toUserName);

		final Optional<ChatMessage> cm = fromUser.flatMap(fu -> {
			return toUser.map(tu -> {
				ChatMessage chatMessage = new ChatMessage();
				MessageCipher messageCipher = myConversionService
						.convertToMessageCipher(messageCipherDTO);
				messageCipher = messageCipherRepository.save(messageCipher);
				chatMessage.setMessageCipher(messageCipher);
				chatMessage.setFromUser(fu);
				chatMessage.setToUser(tu);
				chatMessage = chatMessageRepository.save(chatMessage);
				return chatMessage;
			});
		});
		return modelMapper.map(cm, ChatMessageDTO.class);
	}

	@Override
	@Transactional(readOnly = true)
	public List<ChatMessageDTO> getAllMessages(final String fromUser,
			final String toUser) {
		final List<ChatMessage> chatMessages = chatMessageRepository
				.getAllMessages(fromUser, toUser);
		final List<ChatMessageDTO> chatMessageDTOs = myConversionService
				.convertToChatMessageDTOs(chatMessages);
		return chatMessageDTOs;
	}

	@Override
	@Transactional(readOnly = true)
	public List<ReencryptionDTO> getAllMessagesForReencryption(
			final String fromUser, final String toUser) {
		return myConversionService.convertToReencryptionDTOs(
				chatMessageRepository.getAllMessages(fromUser, toUser));
	}

	@Override
	@Transactional(readOnly = true)
	public List<ChatMessageDTO> getMessagePage(final String fromUser,
			final String toUser, final int page, final int size) {
		return myConversionService.convertToChatMessageDTOs(
				chatMessageRepository.getAllMessages(fromUser, toUser,
						PageRequest.of(page, size)));
	}

	@Override
	@Transactional(readOnly = true)
	public List<ChatMessageDTO> getNewMessages(final String fromUser,
			final String toUser, final Instant lastMessageTime) {
		final List<ChatMessage> chatMessages = chatMessageRepository
				.getNewMessages(fromUser, toUser, lastMessageTime);
		return myConversionService.convertToChatMessageDTOs(chatMessages);
	}

	@Override
	public void reencryptMessages(
			final List<ReencryptionDTO> reencryptionDTOs) {
		final List<MessageCipher> messageCiphers = reencryptionDTOs.stream()
				.map(reencryptionDTO -> reencryptionDTO.getMessageCipher())
				.collect(Collectors.toList());
		messageCipherRepository.saveAll(messageCiphers);
	}

}
