package com.atlassian.stash.internal.web.admin;

import com.atlassian.bitbucket.server.FeatureManager;
import com.atlassian.bitbucket.server.StandardFeature;
import com.atlassian.stash.internal.web.soy.StashSoyResponseBuilder;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.bitbucket.validation.annotation.NonNegativeInt;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.Range;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.validation.Validator;
import javax.validation.constraints.NotNull;

@Controller
@RequestMapping("/admin/authentication")
public final class AuthenticationController {

    public static final String VIEW = "bitbucket.internal.admin.authentication";

    private final FeatureManager featureManager;
    private final ApplicationPropertiesService propertiesService;
    private final SpringValidatorAdapter validatorAdapter;

    @Autowired
    public AuthenticationController(ApplicationPropertiesService propertiesService,
                                    FeatureManager featureManager, Validator validator) {
        this.featureManager = featureManager;
        this.propertiesService = propertiesService;
        this.validatorAdapter = new SpringValidatorAdapter(validator);
    }

    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView showSettings() {
        return new StashSoyResponseBuilder(VIEW)
                .put("form", loadAuthSettings())
                .build();
    }

    @RequestMapping(method = RequestMethod.POST)
    public ModelAndView updateSettings(AuthenticationForm settings, Errors errors, RedirectAttributes redirectAttributes) {
        //Max CAPTCHA attempts is the only validated field and it only present when auth CAPTCHA is enabled
        //so only validate if auth CAPTCHA is enabled
        boolean authCaptchaEnabled = featureManager.isEnabled(StandardFeature.AUTH_CAPTCHA);
        if (authCaptchaEnabled) {
            validatorAdapter.validate(settings, errors);
        }

        if (errors.hasErrors()) {
            return new StashSoyResponseBuilder(VIEW)
                    .put("form", settings)
                    .putValidationErrors(errors)
                    .build();
        } else {
            updateAuthSettings(settings, authCaptchaEnabled);
            redirectAttributes.addFlashAttribute("saved", true);
            return new StashSoyResponseBuilder("redirect:/admin/authentication").build();
        }
    }

    private AuthenticationForm loadAuthSettings() {
        AuthenticationForm form = new AuthenticationForm();
        form.publicSignUp = propertiesService.isAllowPublicSignUp();
        form.captchaOnSignUp = propertiesService.isShowCaptchaOnSignUp();
        form.maxCaptchaAttempts = String.valueOf(propertiesService.getMaxCaptchaAttempts());
        return form;
    }

    private void updateAuthSettings(AuthenticationForm form, boolean authCaptchaEnabled) {
        propertiesService.setAllowPublicSignUp(Boolean.TRUE.equals(form.getPublicSignUp()));
        //The show CAPTCHA form field may be disabled because public signup has been switched off
        //in which case we set CAPTCH to false to be consistent with the form behaviour
        if (Boolean.FALSE.equals(form.getPublicSignUp())) {
            propertiesService.setShowCaptchaOnSignUp(false);
        } else {
            propertiesService.setShowCaptchaOnSignUp(Boolean.TRUE.equals(form.getCaptchaOnSignUp()));
        }
        //The value won't be present if it auth CAPTCHA is disabled
        if (authCaptchaEnabled) {
            propertiesService.setMaxCaptchaAttempts(Integer.parseInt(form.getMaxCaptchaAttempts()));
        }
    }

    public static class AuthenticationForm {
        private Boolean publicSignUp;
        private Boolean captchaOnSignUp;
        private String maxCaptchaAttempts;

        // not mandatory (unspecified -> false)
        public Boolean getPublicSignUp() {
            return publicSignUp;
        }

        // not mandatory (unspecified -> false)
        public Boolean getCaptchaOnSignUp() {
            return captchaOnSignUp;
        }

        @NotNull @NonNegativeInt @Range(min = 1, max = 100)
        public String getMaxCaptchaAttempts() {
            return maxCaptchaAttempts;
        }

        public void setPublicSignUp(Boolean publicSignUp) {
            this.publicSignUp = publicSignUp;
        }

        public void setCaptchaOnSignUp(Boolean captchaOnSignUp) {
            this.captchaOnSignUp = captchaOnSignUp;
        }

        public void setMaxCaptchaAttempts(String maxCaptchaAttempts) {
            this.maxCaptchaAttempts = StringUtils.trim(maxCaptchaAttempts);
        }
    }

}

