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

import com.atlassian.fugue.Pair;
import com.atlassian.soy.renderer.SoyException;
import com.atlassian.soy.renderer.SoyTemplateRenderer;
import com.atlassian.stash.internal.language.InternalLanguageService;
import com.atlassian.bitbucket.ui.ContextualFormFragment;
import com.atlassian.bitbucket.ui.ValidationErrors;
import com.atlassian.bitbucket.view.TemplateRenderingException;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.*;

import static com.atlassian.stash.internal.web.soy.StashSoyViewResolver.SERVER_SOY_MODULE_KEY;
import static com.google.common.base.MoreObjects.firstNonNull;

public abstract class AbstractLocaleFormFragment implements ContextualFormFragment {

    private static final String FIELD_LOCALE = "locale";
    private static final String LOCALE_TEMPLATE = "bitbucket.internal.feature.locale.field";

    private final InternalLanguageService languageService;
    private final SoyTemplateRenderer soyTemplateRenderer;

    protected AbstractLocaleFormFragment(InternalLanguageService languageService, SoyTemplateRenderer soyTemplateRenderer) {
        this.languageService = languageService;
        this.soyTemplateRenderer = soyTemplateRenderer;
    }

    protected abstract Pair<Locale, List<Map<String, Object>>> load(Map<String, Object> context);

    protected abstract void save(Map<String, Object> context, Locale locale);

    @Override
    public void doView(Appendable appendable, Map<String, Object> context) throws IOException {
        Pair<Locale, List<Map<String, Object>>> data = load(context);
        try {
            soyTemplateRenderer.render(appendable, SERVER_SOY_MODULE_KEY, LOCALE_TEMPLATE,
                    ImmutableMap.of("languages", Iterables.concat(data.right(),
                            UserLocaleWebUtils.toSoy(languageService.getLanguages(), data.left().toString())
                    ), "errors", firstNonNull(context.get("errors"), Collections.emptyMap()))
            );
        } catch (SoyException e) {
            throw new TemplateRenderingException("Failed to render " + LOCALE_TEMPLATE, e);
        }
    }

    @Override
    public void validate(Map<String, String[]> requestParams, ValidationErrors errors, Map<String, Object> context) {
        try {
            getLocale(requestParams);
        } catch (IllegalArgumentException e) {
            errors.addFieldError(FIELD_LOCALE, e.getMessage());
        }
    }

    @Override
    public void doError(Appendable appendable, Map<String, String[]> requestParams, Map<String, Collection<String>> fieldErrors, Map<String, Object> context) throws IOException {
        doView(appendable, ImmutableMap.<String, Object>builder()
                        .putAll(context)
                        .put("errors", fieldErrors)
                        .build()
        );
    }

    @Override
    public void execute(Map<String, String[]> requestParams, Map<String, Object> context) {
        save(context, getLocale(requestParams));
    }

    protected Locale getLocale(Map<String, String[]> requestParams) {
        String[] locale = requestParams.get(FIELD_LOCALE);
        // We're sending in ' ' for the default settings (due to html not supporting blank)
        return locale.length > 0 ? LocaleUtils.toLocale(StringUtils.trimToNull(locale[0])) : null;
    }
}
