package org.codiki.account;

import java.io.IOException;
import java.security.Principal;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codiki.core.entities.dto.ImageDTO;
import org.codiki.core.entities.dto.PasswordWrapperDTO;
import org.codiki.core.entities.dto.UserDTO;
import org.codiki.core.entities.persistence.User;
import org.codiki.core.repositories.ImageRepository;
import org.codiki.core.repositories.UserRepository;
import org.codiki.core.security.CustomAuthenticationProvider;
import org.codiki.core.services.FileUploadService;
import org.codiki.core.services.UserService;
import org.codiki.core.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Service
public class AccountService {

	@Autowired
	private CustomAuthenticationProvider authenticationProvider;
	
	@Autowired
	private UserService userService;
	
	@Autowired
	private UserRepository userRepository;

	@Autowired
	private FileUploadService fileUploadService;
	
	@Autowired
	private ImageRepository imageRepository;
	
	public User authenticate(final User pUser) throws BadCredentialsException {
		final User user = userService.checkCredentials(pUser.getEmail(), pUser.getPassword());
		
		authenticationProvider.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
		
		return user;
	}
	
	public void changePassword(final User pUser, final PasswordWrapperDTO pPasswordWrapper,
			final HttpServletResponse pResponse) throws IOException {
		if(pPasswordWrapper.getNewPassword().equals(pPasswordWrapper.getConfirmPassword())) {
			// We fetch the connected user from database to get his hashed password
			final Optional<User> userFromDb = userRepository.findById(pUser.getId());
			if(userFromDb.isPresent() && StringUtils.compareHash(pPasswordWrapper.getOldPassword(),
					userFromDb.get().getPassword())) {
				userFromDb.get().setPassword(StringUtils.hashPassword(pPasswordWrapper.getNewPassword()));
				userRepository.save(userFromDb.get());
			} else {
				pResponse.sendError(HttpServletResponse.SC_FORBIDDEN,
						"Le mot de passe saisi ne correspond pas au votre.");
			}
		} else {
			pResponse.sendError(HttpServletResponse.SC_BAD_REQUEST,
					"Le mot de passe saisi ne correspond pas au votre.");
		}
	}
	
	public String uploadFile(final MultipartFile pFile, final HttpServletRequest pRequest,
			final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
		final String avatarFileName = fileUploadService.uploadProfileImage(pFile);
		
		final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
		if(connectedUser.isPresent()) {
			final Optional<User> userFromDb = userRepository.findById(connectedUser.get().getId());
			if(userFromDb.isPresent()) {
				userFromDb.get().setImage(avatarFileName);
				userRepository.save(userFromDb.get());
			} else {
				pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
			}
		} else {
			pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
		}
		
		return avatarFileName;
	}

	public Resource loadAvatar(final String pAvatarFileName) {
		return fileUploadService.loadAvatar(pAvatarFileName);
	}
	
	public List<ImageDTO> getUserImages(final HttpServletRequest pRequest, final HttpServletResponse pResponse,
			final Principal pPrincipal) throws IOException {
		List<ImageDTO> result = new LinkedList<>();
		
		final Optional<User> connectedUser = userService.getUserByPrincipal(pPrincipal);
		if(connectedUser.isPresent()) {
			result = imageRepository.getByUserId(connectedUser.get().getId())
					.stream().map(ImageDTO::new).collect(Collectors.toList());
		} else {
			pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
		}
		
		return result;
	}

	public void signin(final User pUser, final HttpServletResponse pResponse) throws IOException {
		if(pUser.getName() == null || pUser.getEmail() == null || pUser.getPassword() == null || "".equals(pUser.getPassword().trim())) {
			pResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);
		} else if(userRepository.findByEmail(pUser.getEmail()).isPresent()) {
			pResponse.sendError(HttpServletResponse.SC_CONFLICT);
		} else {
			User user = new User();

			user.setName(pUser.getName());
			user.setEmail(pUser.getEmail());
			user.setPassword(StringUtils.hashPassword(pUser.getPassword()));
			user.setInscriptionDate(new Date());
			userRepository.save(user);
		}
	}
	
	public void updateUser(final UserDTO pUser, final HttpServletRequest pRequest,
			final HttpServletResponse pResponse, final Principal pPrincipal) throws IOException {
		final Optional<User> connectedUserOpt = userService.getUserByPrincipal(pPrincipal);
		if(connectedUserOpt.isPresent()) {
			final User connectedUser = connectedUserOpt.get();
			
			final Optional<User> userFromDb = userRepository.findByEmail(pUser.getEmail());
			
			/*
			 * If a user is returned by the repository, that's the email adress is used, but
			 * if it is not the same as the connected user, that's the email adress
			 * corresponds to another user. So a 409 error is sent. Otherwise, if no user is
			 * returned by the repository, that's the email adress is free to be used. So,
			 * user can change him email adress.
			 */
			if(userFromDb.isPresent() && !connectedUser.getEmail().equals(userFromDb.get().getEmail())) {
				pResponse.sendError(HttpServletResponse.SC_CONFLICT);
			} else {
				connectedUser.setName(pUser.getName());
				connectedUser.setEmail(pUser.getEmail());
				
				userRepository.save(connectedUser);
			}
		} else {
			pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
		}
	}
}
