package org.iri_research.renkan.controller.admin;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.apache.commons.codec.binary.Hex;
import org.iri_research.renkan.Constants;
import org.iri_research.renkan.RenkanException;
import org.iri_research.renkan.controller.Utils;
import org.iri_research.renkan.forms.UserForm;
import org.iri_research.renkan.forms.UserFormValidator;
import org.iri_research.renkan.models.User;
import org.iri_research.renkan.repositories.GroupsRepository;
import org.iri_research.renkan.repositories.ProjectsRepository;
import org.iri_research.renkan.repositories.UsersRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.HttpClientErrorException;

@Controller
@RequestMapping("/admin/users")
public class UsersAdminController {

    private final Logger logger = LoggerFactory
            .getLogger(UsersAdminController.class);

    @Inject
    private UsersRepository usersRepository;

    @Inject
    private ProjectsRepository projectsRepository;
    
    @Inject
    private GroupsRepository groupsRepository;

    @Resource(name="renkanPasswordEncoder")
    private PasswordEncoder passwordEncoder;
    
    @InitBinder(value = { "user" })
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(new UserFormValidator());
    }

    @InitBinder
    public void initDateBinder(final WebDataBinder dataBinder, final Locale locale) {
        dataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }

    @RequestMapping(value = "", method = RequestMethod.GET, produces = { "text/html;charset=UTF-8" })
    public String usersList(
            Model model,
            @PageableDefault(sort = { "username" }, direction = Direction.DESC, page = 0, value = Constants.PAGINATION_SIZE) Pageable p,
            HttpServletRequest request) {

        Page<User> page = this.usersRepository.findAll(p);

        model.addAttribute("page", page);
        model.addAttribute("baseUrl", Utils.buildBaseUrl(request));
        model.addAttribute("projectsCount",
                this.projectsRepository.getCountByUser());

        return "admin/usersList";
    }

    @RequestMapping(value = "/edit/", method = RequestMethod.GET, produces = { "text/html;charset=UTF-8" })
    public String editUser(Model model) {
        return editUser(model, null);
    }

    @RequestMapping(value = "/edit/{userId}", method = RequestMethod.GET, produces = { "text/html;charset=UTF-8" })
    public String editUser(Model model,
            @PathVariable(value = "userId") String userId) {

        UserForm userForm = null;
        User user = null;

        if (userId != null && userId.length() > 0 && !"_".equals(userId)) {
            user = this.usersRepository.findOne(userId);
            if (user == null) {
                throw new HttpClientErrorException(HttpStatus.NOT_FOUND,
                        "user " + userId + " not found");
            }
        }
        userForm = new UserForm(user);

        model.addAttribute("user", userForm);
        model.addAttribute("allGroups", this.groupsRepository.findAll());

        return "admin/userEdit";
    }

    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public String saveUser(Model model,
            @ModelAttribute("user") @Valid UserForm userForm,
            BindingResult bindingResult) {

        logger.debug("user title " + userForm.getTitle());
        logger.debug("user description " + userForm.getDescription());

        if (bindingResult.hasErrors()) {
            return "admin/userEdit";
        }

        userForm.setUsersRepository(usersRepository);
        userForm.setPasswordEncoder(passwordEncoder);

        try {
            userForm.save();
        } catch (RenkanException e) {
            throw new HttpClientErrorException(HttpStatus.NOT_FOUND, "user "
                    + userForm.getId()==null?"":userForm.getId() + " not found");
        }

        return "redirect:/admin/users";
    }

    @RequestMapping(value = "/delete/{userId}")
    public String deleteUser(HttpServletRequest request, Model model,
            @PathVariable(value = "userId") String userId,
            @RequestParam(value = "key", required = false) String key,
            @RequestParam(value = "salt", required = false) String salt)
            throws NoSuchAlgorithmException, RenkanException {

        if (userId == null || userId.length() == 0) {
            throw new HttpClientErrorException(HttpStatus.BAD_REQUEST,
                    "Null or empty user id");
        }

        RequestMethod method = RequestMethod.valueOf(request.getMethod());

        Map<String, Integer> nbProj = this.projectsRepository
                .getCountByUser(Arrays.asList(userId));
        if (nbProj.containsKey(userId) && nbProj.get(userId).intValue() > 0) {
            throw new HttpClientErrorException(HttpStatus.BAD_REQUEST,
                    "This user have projects");
        }

        if (RequestMethod.GET.equals(method)) {

            User user = this.usersRepository.findOne(userId);

            if (user == null) {
                throw new HttpClientErrorException(HttpStatus.NOT_FOUND,
                        "user " + userId + " not found");
            }

            SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
            rand.setSeed(System.currentTimeMillis());
            byte[] rawSalt = new byte[50];
            rand.nextBytes(rawSalt);
            String newSalt = Hex.encodeHexString(rawSalt);

            model.addAttribute("userObj", user);
            model.addAttribute("salt", newSalt);
            model.addAttribute("key", user.getKey(newSalt));

            return "admin/userDeleteConfirm";

        } else if (RequestMethod.POST.equals(method) && key != null
                && !key.isEmpty() && salt != null && !salt.isEmpty()) {

            if (userId != null && userId.length() > 0) {

                User user = this.usersRepository.findOne(userId);
                if (user != null) {
                    if (user.checkKey(key, salt)) {
                        this.usersRepository.delete(userId);
                    } else {
                        throw new HttpClientErrorException(
                                HttpStatus.BAD_REQUEST, "Key not ckecked");
                    }
                }

            }
            return "redirect:/admin/users";

        } else {
            throw new HttpClientErrorException(HttpStatus.BAD_REQUEST,
                    "Bad request method or parameters");
        }

    }

}
