package de.champonthis.buntspecht.controller.admin;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.querydsl.core.QueryResults;

import de.champonthis.buntspecht.businesslogic.UserManager;
import de.champonthis.buntspecht.controller.BaseController;
import de.champonthis.buntspecht.controller.admin.validation.UserValidator;
import de.champonthis.buntspecht.controller.model.UserPasswordModel;
import de.champonthis.buntspecht.controller.support.EntityResponseStatusException;
import de.champonthis.buntspecht.controller.support.RequestBodyErrors;
import de.champonthis.buntspecht.controller.validation.PasswordModelValidator;
import de.champonthis.buntspecht.model.User;
import jakarta.transaction.Transactional;

@RestController
@RequestMapping("/users/manage")
public class UserManagementController extends BaseController {

	@Autowired
	private UserManager userManager;
	@Autowired
	private UserValidator userValidator;
	@Autowired
	private PasswordModelValidator passwordModelValidator;

	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@GetMapping
	@Transactional
	public QueryResults<User> fetch(
			@RequestParam("limit") Optional<Long> limitParameter,
			@RequestParam("offset") Optional<Long> offsetParameter,
			@RequestParam("sort") Optional<String> sort,
			@RequestParam("descending") Optional<Boolean> descending,
			@RequestParam("filter") Optional<String> search) {
		return userManager.fetch(limitParameter.orElse(15L), offsetParameter.orElse(0L), sort.orElse("username"),
				descending.orElse(false), search.orElse(""));
	}

	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@GetMapping("/pick")
	@Transactional
	public List<User> pick(
			@RequestParam("filter") Optional<String> search) {
		return userManager.fetch(5L, 0L, "", false, search.orElse("")).getResults();
	}

	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@GetMapping("/{username}")
	@Transactional
	public User get(@PathVariable("username") String username) {
		User user = userManager.getByUsername(username);

		if (user == null) {
			throw new EntityResponseStatusException(HttpStatus.NO_CONTENT);
		}

		return user;
	}

	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@PostMapping
	@Transactional
	public User create(@RequestBody User user) {
		Errors errors = new RequestBodyErrors(user);
		userValidator.validateNew(user, errors);

		if (errors.hasErrors()) {
			throw new EntityResponseStatusException(errors.getAllErrors(), HttpStatus.CONFLICT);
		}

		if (user.getLocale() == null) {
			user.setLocale("de");
		}

		return userManager.save(user);
	}

	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@PatchMapping
	@Transactional
	public User update(@RequestBody User user) {
		Errors errors = new RequestBodyErrors(user);
		userValidator.validate(user, errors);

		if (errors.hasErrors()) {
			throw new EntityResponseStatusException(errors.getAllErrors(), HttpStatus.CONFLICT);
		}

		if (getCurrentUsername().equals(user.getUsername()) && user.getRoles().indexOf("ROLE_ADMIN") == -1) {
			user.getRoles().add("ROLE_ADMIN");
		}

		return userManager.save(user);
	}

	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@DeleteMapping("/{username}")
	@Transactional
	public void delete(@PathVariable("username") String username) {
		User user = userManager.getByUsername(username);

		if (user == null || getCurrentUsername().equals(username)) {
			throw new EntityResponseStatusException(HttpStatus.NOT_MODIFIED);
		}

		userManager.delete(user);
	}

	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@PostMapping("/{username}/password")
	public void password(@PathVariable("username") String username,
			@RequestBody UserPasswordModel passwordModel) {

		Errors errors = new RequestBodyErrors(passwordModel);

		User user = userManager.getByUsername(username);

		if (user == null) {
			throw new EntityResponseStatusException(HttpStatus.NO_CONTENT);
		}

		passwordModelValidator.validate(passwordModel, errors);

		if (errors.hasErrors()) {
			throw new EntityResponseStatusException(errors.getAllErrors(), HttpStatus.CONFLICT);
		}

		userManager.setPassword(user.getUsername(), passwordModel.getPassword());
	}

}
