package org.iri_research.renkan.controller.admin;

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

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.GroupForm;
import org.iri_research.renkan.forms.GroupFormValidator;
import org.iri_research.renkan.models.Group;
import org.iri_research.renkan.repositories.GroupsRepository;
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.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/groups")
public class GroupsAdminController {

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

    @Inject
    private GroupsRepository groupsRepository;

    @Inject
    private UsersRepository usersRepository;


    @InitBinder(value = { "group" })
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(new GroupFormValidator());
    }

    @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 groupsList(
            Model model,
            @PageableDefault(sort = { "title" }, direction = Direction.DESC, page = 0, value = Constants.PAGINATION_SIZE) Pageable p,
            HttpServletRequest request) {

        Page<Group> page = this.groupsRepository.findAll(p);

        model.addAttribute("page", page);
        model.addAttribute("baseUrl", Utils.buildBaseUrl(request));
        //TODO: add user count

        return "admin/groupsList";
    }

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

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

        GroupForm groupForm = null;
        Group group = null;

        if (groupId != null && groupId.length() > 0 && !"_".equals(groupId)) {
            group = this.groupsRepository.findOne(groupId);
            if (group == null) {
                throw new HttpClientErrorException(HttpStatus.NOT_FOUND,
                        "group " + groupId + " not found");
            }
        }
        groupForm = new GroupForm(group);

        model.addAttribute("group", groupForm);
        model.addAttribute("allUsers", this.usersRepository.findAll());

        return "admin/groupEdit";
    }

    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public String saveGroup(Model model,
            @ModelAttribute("group") @Valid GroupForm groupForm,
            BindingResult bindingResult) {

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

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

        groupForm.setGroupsRepository(groupsRepository);

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

        return "redirect:/admin/groups";
    }

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

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

        RequestMethod method = RequestMethod.valueOf(request.getMethod());
        
        //TODO: check that group have no user


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

            Group group = this.groupsRepository.findOne(groupId);

            if (group == null) {
                throw new HttpClientErrorException(HttpStatus.NOT_FOUND,
                        "group " + groupId + " 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("groupObj", group);
            model.addAttribute("salt", newSalt);
            model.addAttribute("key", group.getKey(newSalt));

            return "admin/groupDeleteConfirm";

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

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

                Group group = this.groupsRepository.findOne(groupId);
                if (group != null) {
                    if (group.checkKey(key, salt)) {
                        this.groupsRepository.delete(groupId);
                    } else {
                        throw new HttpClientErrorException(
                                HttpStatus.BAD_REQUEST, "Key not ckecked");
                    }
                }

            }
            return "redirect:/admin/groups";

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

    }

}
