package cz.utb.tadis.auth.controller;

import cz.utb.tadis.common.http.ApiResponse;
import cz.utb.tadis.auth.http.UpdateOtherProfileRequest;
import cz.utb.tadis.auth.http.UpdateProfileRequest;
import cz.utb.tadis.auth.model.Role;
import cz.utb.tadis.auth.model.User;
import cz.utb.tadis.auth.model.UserProfile;
import cz.utb.tadis.auth.repository.RoleRepository;
import cz.utb.tadis.auth.repository.UserRepository;
import cz.utb.tadis.security.JwtUtil;
import cz.utb.tadis.security.SecurityContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    UserRepository userRepository;

    @Autowired
    RoleRepository roleRepository;

    @Autowired
    JwtUtil jwtUtil;

    @Autowired
    SecurityContextUtil securityContextUtil;

    @Autowired
    BCryptPasswordEncoder passwordEncoder;

    @GetMapping("/profile")
    public ResponseEntity<?> getUser(){
        User user = securityContextUtil.getUserFromContext();
        UserProfile userProfile = UserProfile.convertUser(user);
        return new ResponseEntity(userProfile, HttpStatus.OK);
    }

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/profiles")
    public ResponseEntity<?> getUsers(){
        List<User> users = userRepository.findAll();
        List<UserProfile> userProfiles = new ArrayList<>();
        for(User user: users){
            userProfiles.add(UserProfile.convertUser(user));
        }
        return new ResponseEntity(userProfiles, HttpStatus.OK);
    }

    @PreAuthorize("hasAnyRole('USER', 'EDITOR', 'ADMIN')")
    @PutMapping("/update")
    public ResponseEntity<?> update(@Valid @RequestBody UpdateProfileRequest updateRequest){
        User loggedUser = securityContextUtil.getUserFromContext();

        Optional<User> userWithNewEmail = userRepository.findByEmail(updateRequest.getEmail());
        if(userWithNewEmail.isPresent() && userWithNewEmail.get().getId() != loggedUser.getId()){
            return new ResponseEntity(new ApiResponse(HttpStatus.CONFLICT, "This email adress is already taken."), HttpStatus.CONFLICT);
        }

        loggedUser.setEmail(updateRequest.getEmail());
        loggedUser.setFirstName(updateRequest.getFirstName());
        loggedUser.setLastName(updateRequest.getLastName());
        loggedUser.setCompany(updateRequest.getCompany());
        loggedUser.setPosition(updateRequest.getPosition());

        String newPassword = updateRequest.getNewPassword();
        if (newPassword != null && newPassword.length() > 0){
            if(passwordEncoder.matches(updateRequest.getOldPassword(), loggedUser.getPassword())){
                loggedUser.setPassword(passwordEncoder.encode(updateRequest.getNewPassword()));
            }
            else{
                return new ResponseEntity(new ApiResponse(HttpStatus.UNAUTHORIZED, "Wrong Password", "Profile hasn't been updated due to wrong password."), HttpStatus.UNAUTHORIZED);
            }
        }
        userRepository.save(loggedUser);

        return new ResponseEntity(UserProfile.convertUser(loggedUser), HttpStatus.OK);
    }

    @PreAuthorize("hasRole('ADMIN')")
    @PutMapping("/update/{id}")
    public ResponseEntity<?> updateUser(@PathVariable("id") Long id, @Valid @RequestBody UpdateOtherProfileRequest updateRequest){
        if (securityContextUtil.getUserFromContext().getId() == id){
            return new ResponseEntity(new ApiResponse(HttpStatus.INTERNAL_SERVER_ERROR, "Couldn't update profile."), HttpStatus.INTERNAL_SERVER_ERROR);
        }

        Optional<User> userWithNewEmail = userRepository.findByEmail(updateRequest.getEmail());
        if(userWithNewEmail.isPresent() && userWithNewEmail.get().getId() != id){
            return new ResponseEntity(new ApiResponse(HttpStatus.CONFLICT, "This email adress is already taken."), HttpStatus.CONFLICT);
        }

        User user = userRepository.findByIdAndActive(id, true).get();

        user.setEmail(updateRequest.getEmail());
        user.setFirstName(updateRequest.getFirstName());
        user.setLastName(updateRequest.getLastName());
        user.setCompany(updateRequest.getCompany());
        user.setPosition(updateRequest.getPosition());

        String newPassword = updateRequest.getNewPassword();
        if (newPassword != null && newPassword.length() > 0){
            user.setPassword(passwordEncoder.encode(newPassword));
        }

        Role newRole = roleRepository.findByName(updateRequest.getRole()).get();
        user.setRole(newRole);

        userRepository.save(user);

        return getUsers();
    }

    @PreAuthorize("hasAnyRole('USER', 'EDITOR', 'ADMIN')")
    @DeleteMapping("/delete")
    public ResponseEntity<?> delete(){
        User user = securityContextUtil.getUserFromContext();
        if ("ROLE_ADMIN".equals(user.getRole().getName())){
            List<User> adminUsers = userRepository.findAllByRoleName("ROLE_ADMIN");
            if (adminUsers.size() < 2) {
                return new ResponseEntity(new ApiResponse(HttpStatus.CONFLICT, "Profile couldn't been deleted since it's only existing Admin profile."), HttpStatus.CONFLICT);
            }
        }
        this.userRepository.deleteById(user.getId());
        return new ResponseEntity(new ApiResponse(HttpStatus.OK, "Your profile has been successfully deleted."), HttpStatus.OK);
    }

    @PreAuthorize("hasRole('ADMIN')")
    @DeleteMapping("/delete/{id}")
    public ResponseEntity<?> deleteUser(@PathVariable("id") Long id){
        User user = securityContextUtil.getUserFromContext();
        if (user.getId() == id){
            return new ResponseEntity(new ApiResponse(HttpStatus.CONFLICT, "Couldn't delete profile."), HttpStatus.CONFLICT);
        }
        this.userRepository.deleteById(id);
        return getUsers();
    }

    @PreAuthorize("hasRole('ADMIN')")
    @PutMapping("/approve/")
    public ResponseEntity<?> approveUsers(@Valid @RequestBody List<UserProfile> userProfiles){
        for(UserProfile profile: userProfiles){
            Optional<User> optUser = userRepository.findById(profile.getId());
            if (optUser.isPresent()){
                User user = optUser.get();
                user.setActive(true);
                userRepository.save(user);
            }
        }
        return getUsers();
    }

    @PreAuthorize("hasRole('ADMIN')")
    @PutMapping("/reject/")
    public ResponseEntity<?> rejectUsers(@Valid @RequestBody List<UserProfile> userProfiles){
        for(UserProfile profile: userProfiles){
            userRepository.deleteByIdAndActive(profile.getId(), false);
        }
        return getUsers();
    }

}
