package org.codiki.account;

import java.io.IOException;
import java.security.Principal;
import java.util.Optional;

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

import org.codiki.core.entities.dto.PasswordWrapperDTO;
import org.codiki.core.entities.dto.UserDTO;
import org.codiki.core.entities.dto.View;
import org.codiki.core.entities.persistence.User;
import org.codiki.core.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.annotation.JsonView;

@RestController
@RequestMapping("/api/account")
public class AccountController {
	/** Account service. */
	private AccountService accountService;
	/** User service. */
	private UserService userService;

	/**
	 * Constructor.
	 * @param accountService Account service.
	 * @param userService User service.
	 */
	public AccountController(AccountService accountService, UserService userService) {
		this.accountService = accountService;
		this.userService = userService;
	}

	@JsonView(View.UserDTO.class)
	@PostMapping("/login")
	public User login(@RequestBody final User pUser, HttpServletResponse pResponse) throws BadCredentialsException {
		User result = null;

		try {
			result = accountService.authenticate(pUser);
		} catch(BadCredentialsException ex) {
			pResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
		}

		return result;
	}
	
	@GetMapping("/logout")
	public void logout(final HttpServletRequest request, final HttpServletResponse response) {
		final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		if(auth != null) {
			new SecurityContextLogoutHandler().logout(request, response, auth);
		}
	}
	
	/**
	 * Updates the user password.
	 * 
	 * @param pPasswordWrapper
	 *            The object which contains the old password for verification and
	 *            the new password to set to the user.
	 * @param pResponse
	 *            The reponse injected by Spring.
	 * @throws IOException
	 *             If the old password doesn't match to the user password in
	 *             database.
	 */
	@PutMapping("/changePassword")
	public void changePassword(@RequestBody final PasswordWrapperDTO pPasswordWrapper,
			final HttpServletResponse pResponse,
			final Principal pPrincipal) {
		int httpResponseCode = userService.getUserByPrincipal(pPrincipal)
				.map(user -> {
					try {
						return accountService.changePassword(user, pPasswordWrapper);
					} catch (IllegalArgumentException e) {
						return HttpServletResponse.SC_BAD_REQUEST;
					}
				})
				.orElse(HttpServletResponse.SC_UNAUTHORIZED);

		pResponse.setStatus(httpResponseCode);
	}
	
	@PostMapping("/signin")
	public void signin(@RequestBody final User pUser, final HttpServletResponse pResponse) throws IOException {
		accountService.signin(pUser, pResponse);
	}
	
	@PutMapping("/")
	public void update(@RequestBody UserDTO pUser, HttpServletResponse pResponse, Principal pPrincipal)
			throws IOException {
		accountService.updateUser(pUser, pResponse, pPrincipal);
	}
}
