package com.atlassian.stash.internal.plugin.hooks.approvers;

import com.atlassian.soy.renderer.SoyException;
import com.atlassian.soy.renderer.SoyTemplateRenderer;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.ui.ContextualFormFragment;
import com.atlassian.bitbucket.ui.ValidationErrors;
import com.atlassian.bitbucket.view.TemplateRenderingException;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;

import static com.atlassian.stash.internal.plugin.hooks.approvers.RequiredApproversHelper.MODULE_KEY;
import static com.atlassian.stash.internal.plugin.hooks.approvers.RequiredApproversHelper.MAX_APPROVERS;
import static java.util.Objects.requireNonNull;

public class RequiredApproversFormFragment implements ContextualFormFragment {

    private static final String FRAGMENT_TEMPLATE = "bitbucket.internal.requiredApprovers.fragment";
    private static final String FIELD_KEY = "requiredApproversCount";
    private static final String FIELD_MAX_VALUE = "maxRequiredApproversCount";
    private static final String FIELD_ERRORS = "fieldErrors";

    private final I18nService i18nService;
    private final SoyTemplateRenderer soyTemplateRenderer;
    private final RequiredApproversHelper helper;

    public RequiredApproversFormFragment(I18nService i18nService, RequiredApproversHelper helper,
                                         SoyTemplateRenderer soyTemplateRenderer) {
        this.i18nService = i18nService;
        this.helper = helper;
        this.soyTemplateRenderer = soyTemplateRenderer;
    }

    @Override
    public void doView(@Nonnull Appendable appendable, @Nonnull Map<String, Object> context) throws IOException {
        requireNonNull(appendable, "appendable");
        requireNonNull(context, "context");

        Repository repository = (Repository) context.get("repository");
        context.put(FIELD_KEY, helper.getRequiredApproversCount(repository));
        context.put(FIELD_MAX_VALUE, MAX_APPROVERS);
        renderView(appendable, context);
    }

    @Override
    public void doError(@Nonnull Appendable appendable, @Nonnull Map<String, String[]> requestParams,
                        @Nonnull Map<String, Collection<String>> fieldErrors, @Nonnull Map<String, Object> context)
            throws IOException {
        requireNonNull(appendable, "appendable");
        requireNonNull(requestParams, "requestParams");
        requireNonNull(fieldErrors, "fieldErrors");
        requireNonNull(context, "context");

        if (fieldErrors.containsKey(FIELD_KEY)) {
            context.put(FIELD_KEY, 1);
        } else {
            // validate() has already succeeded, so we know this will be a valid integer
            String[] values = requestParams.get(FIELD_KEY);
            int count = (values == null) ? 0 : Integer.parseInt(values[0]);
            context.put(FIELD_KEY, count);
        }
        context.put(FIELD_MAX_VALUE, MAX_APPROVERS);
        context.put(FIELD_ERRORS, fieldErrors);
        renderView(appendable, context);
    }

    @Override
    public void validate(@Nonnull Map<String, String[]> requestParams, @Nonnull ValidationErrors errors,
                         @Nonnull Map<String, Object> context) {
        requireNonNull(requestParams, "requestParams");
        requireNonNull(errors, "errors");
        requireNonNull(context, "context");

        String[] values = requestParams.get(FIELD_KEY);
        if (values != null) {
            if (values.length == 1) {
                try {
                    int n = Integer.parseInt(values[0]);
                    if (n < 0) {
                        errors.addFieldError(FIELD_KEY, i18nService.getMessage("bitbucket.requiredApprovers.count.negativeNumber"));
                    } else if (n > MAX_APPROVERS) {
                        errors.addFieldError(FIELD_KEY, i18nService.getMessage("bitbucket.requiredApprovers.count.tooLarge", MAX_APPROVERS));
                    }
                } catch (NumberFormatException e) {
                    errors.addFieldError(FIELD_KEY, i18nService.getMessage("bitbucket.requiredApprovers.count.notANumber"));
                }
            } else {
                errors.addFieldError(FIELD_KEY, i18nService.getMessage("bitbucket.requiredApprovers.count.tooManyValues"));
            }
        }
    }

    @Override
    public void execute(@Nonnull Map<String, String[]> requestParams, @Nonnull Map<String, Object> context) {
        requireNonNull(requestParams, "requestParams");
        requireNonNull(context, "context");

        Repository repository = (Repository) context.get("repository");

        // the validation was done in validate(), so the number of required approvers is not re-checked
        String[] values = requestParams.get(FIELD_KEY);
        Integer count = values == null ? null : Integer.parseInt(values[0]);
        helper.saveRequiredApproversCount(repository, count);
    }

    private void renderView(Appendable appendable, Map<String, Object> context) {
        try {
            soyTemplateRenderer.render(appendable, MODULE_KEY, FRAGMENT_TEMPLATE, context);
        } catch (SoyException e) {
            throw new TemplateRenderingException("Failed to render " + FRAGMENT_TEMPLATE, e);
        }
    }

}
