package org.iri_research.renkan.controller.admin;

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

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.SpaceForm;
import org.iri_research.renkan.forms.SpaceFormValidator;
import org.iri_research.renkan.models.Space;
import org.iri_research.renkan.repositories.ProjectsRepository;
import org.iri_research.renkan.repositories.SpacesRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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/spaces")
public class SpacesAdminController {

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

    @Autowired
    private SpacesRepository spacesRepository;
    @Autowired
    private ProjectsRepository projectsRepository;

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

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

        Page<Space> page = this.spacesRepository.findAll(p);

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

        return "admin/spacesList";
    }

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

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

        SpaceForm spaceForm = null;

        if (spaceId == null || spaceId.length() == 0 || "_".equals(spaceId)) {
            spaceForm = new SpaceForm();
        } else {
            Space space = this.spacesRepository.findOne(spaceId);
            if (space == null) {
                throw new HttpClientErrorException(HttpStatus.NOT_FOUND,
                        "space " + spaceId + " not found");
            }
            spaceForm = new SpaceForm(space);
        }

        model.addAttribute("space", spaceForm);

        return "admin/spaceEdit";
    }

    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public String saveSpace(Model model,
            @ModelAttribute("space") @Valid SpaceForm spaceForm,
            BindingResult bindingResult) {

        logger.debug("space title " + spaceForm.getTitle());
        logger.debug("space description " + spaceForm.getDescription());

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

        spaceForm.setSpacesRepository(spacesRepository);

        try {
            spaceForm.save();
        } catch (RenkanException e) {
            throw new HttpClientErrorException(HttpStatus.NOT_FOUND, "space "
                    + spaceForm.getId() + " not found");
        }

        return "redirect:/admin/spaces";
    }

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

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

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

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

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

            Space space = this.spacesRepository.findOne(spaceId);

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

            return "admin/spaceDeleteConfirm";
        } else if (RequestMethod.POST.equals(method) && key != null
                && !key.isEmpty() && salt != null && !salt.isEmpty()) {

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

                Space space = this.spacesRepository.findOne(spaceId);
                if (space != null) {
                    if (space.checkKey(key, salt)) {
                        this.spacesRepository.delete(spaceId);
                    } else {
                        throw new HttpClientErrorException(
                                HttpStatus.BAD_REQUEST, "Key not ckecked");
                    }
                }

            }
            return "redirect:/admin/spaces";

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

    }

}
