package kr.co.tobby48.medihand.controllers;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.CrossOrigin;
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 kr.co.tobby48.medihand.models.ERole;
import kr.co.tobby48.medihand.models.Role;
import kr.co.tobby48.medihand.models.User;
import kr.co.tobby48.medihand.payload.request.LoginRequest;
import kr.co.tobby48.medihand.payload.request.PasswordUpdateRequest;
import kr.co.tobby48.medihand.payload.request.SignupRequest;
import kr.co.tobby48.medihand.payload.response.JwtResponse;
import kr.co.tobby48.medihand.payload.response.MessageResponse;
import kr.co.tobby48.medihand.repository.RoleRepository;
import kr.co.tobby48.medihand.repository.UserRepository;
import kr.co.tobby48.medihand.security.jwt.JwtUtils;
import kr.co.tobby48.medihand.security.services.UserDetailsImpl;

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/auth")
public class AuthController {
	@Autowired
	AuthenticationManager authenticationManager;

	@Autowired
	UserRepository userRepository;

	@Autowired
	RoleRepository roleRepository;

	@Autowired
	PasswordEncoder encoder;

	@Autowired
	JwtUtils jwtUtils;



	@PutMapping("/update")
	public ResponseEntity<?> updateAuthenticate(@Valid @RequestBody PasswordUpdateRequest passwordUpdateRequest) {

		User user = userRepository.findByUsername(passwordUpdateRequest.getUsername()).orElseGet(User::new);
		authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(passwordUpdateRequest.getUsername(), passwordUpdateRequest.getOriginal()));
//		if( passwordUpdateRequest.getPassword().equals(passwordUpdateRequest.getConfirm()) &&
//				user != null &&
//				encoder.encode(passwordUpdateRequest.getOriginal()).equals(user.getPassword()) ) {
		if( passwordUpdateRequest.getPassword().equals(passwordUpdateRequest.getConfirm()) ) {
			user.setPassword(encoder.encode(passwordUpdateRequest.getPassword()));
			userRepository.save(user);
			return ResponseEntity.ok(new MessageResponse("update"));
			
//			Authentication authentication = authenticationManager.authenticate(
//					new UsernamePasswordAuthenticationToken(passwordUpdateRequest.getUsername(), passwordUpdateRequest.getPassword()));
//
//			SecurityContextHolder.getContext().setAuthentication(authentication);
//			String jwt = jwtUtils.generateJwtToken(authentication);
//
//			UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();    
//			List<String> roles = userDetails.getAuthorities().stream()
//					.map(item -> item.getAuthority())
//					.collect(Collectors.toList());
//
//			return ResponseEntity.ok(new JwtResponse(jwt, 
//					userDetails.getId(), 
//					userDetails.getUsername(), 
//					roles));
		}
		return ResponseEntity.internalServerError().build();
	}

	@PostMapping("/signin")
	public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {

		Authentication authentication = authenticationManager.authenticate(
				new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));

		SecurityContextHolder.getContext().setAuthentication(authentication);
		String jwt = jwtUtils.generateJwtToken(authentication);

		UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();    
		List<String> roles = userDetails.getAuthorities().stream()
				.map(item -> item.getAuthority())
				.collect(Collectors.toList());
		
		User user = userRepository.getById(userDetails.getId());

		return ResponseEntity.ok(new JwtResponse(jwt, 
				userDetails.getId(), 
				userDetails.getUsername(), 
				roles,
				user));

	}

	@PostMapping("/signup")
	public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signUpRequest) {
		if (userRepository.existsByUsername(signUpRequest.getUsername())) {
			return ResponseEntity
					.badRequest()
					.body(new MessageResponse("Error: Username is already taken!"));
		}

//		if (userRepository.existsByEmail(signUpRequest.getEmail())) {
//			return ResponseEntity
//						.badRequest()
//						.body(new MessageResponse("Error: Email is already in use!"));
//		}
		// Create new user's account
		User user = new User(signUpRequest.getUsername(), 
				encoder.encode(signUpRequest.getPassword()));

		Set<String> strRoles = signUpRequest.getRole();
		Set<Role> roles = new HashSet<>();

		if (strRoles == null) {
			Role userRole = roleRepository.findByName(ERole.ROLE_USER)
					.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
			roles.add(userRole);
		} else {
			strRoles.forEach(role -> {
				switch (role) {
				case "admin":
					Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN)
					.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
					roles.add(adminRole);

					break;
//        		case "mod":
//          		Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR)
//              		.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
//          		roles.add(modRole);
//
//          		break;
				default:
					Role userRole = roleRepository.findByName(ERole.ROLE_USER)
					.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
					roles.add(userRole);
				}
			});
		}

		user.setRoles(roles);
		userRepository.save(user);

		return ResponseEntity.ok(new MessageResponse("User registered successfully!"));
	}
}
