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

import com.atlassian.bitbucket.i18n.I18nService;
import com.google.common.base.MoreObjects;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;

import java.text.MessageFormat;
import java.util.Locale;

/**
 * An implementation of the Spring {@code MessageSource} interface which uses the {@link I18nService} to resolve
 * and format requested messages.
 * <p>
 * <b>Warning:</b> This class is eager preinstantiated by Spring in a special bit of initialisation code. <i>Do not use
 * component scanning and autowiring for this class.</i> Doing so causes unexpected things in the initialisation order
 * for the overall Spring context and can result in strange behaviours that are <i>very</i> difficult to debug. See the
 * comments in {@code HomeLockAcquirer} for more details.
 */
public class I18nMessageSource implements MessageSource {

    public static final String[] EMPTY_STRINGS = new String[0];

    private final I18nService i18nService;

    public I18nMessageSource(I18nService i18nService) {
        this.i18nService = i18nService;
    }

    @Override
    public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
        // For some reason known only to the dickhead who wrote the code, but args can be null
        args = MoreObjects.firstNonNull(args, new Object[0]);
        int argCount = ArrayUtils.getLength(args);
        for (int i = 0; i < argCount; ++i) {
            Object arg = args[i];
            if (arg instanceof MessageSourceResolvable) {
                args[i] = getMessage((MessageSourceResolvable) arg, locale);
            }
        }

        // Make sure we don't ignore the user's locale setting
        // This is to avoid having to set the holder for each request based on the user's locale
        // We could even always ignore the locale here completely - Spring isn't going to be passing anything interesting
        String message = locale.equals(LocaleContextHolder.getLocale())
                ? i18nService.getText(code, defaultMessage, args)
                : i18nService.getText(locale, code, defaultMessage, args);
        if (!code.equals(message)) {
            return message;
        }
        throw new NoSuchMessageException(code, locale);
    }

    @Override
    public String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
        return getMessage(code, args, null, locale);
    }

    @Override
    public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
        String[] codes = resolvable.getCodes();
        if (codes == null) {
            codes = EMPTY_STRINGS;
        }

        for (String code : codes) {
            try {
                String computedMessage = getMessage(code, resolvable.getArguments(), null, locale);
                if (!code.equals(computedMessage)) {
                    return computedMessage;
                }
            } catch (NoSuchMessageException e) {
                // Try defaultMessage before failing
            }
        }

        if (resolvable.getDefaultMessage() != null) {
            return MessageFormat.format(resolvable.getDefaultMessage(), resolvable.getArguments());
        }

        throw new NoSuchMessageException(codes.length > 0 ? codes[codes.length - 1] : null, locale);
    }
}
