package com.atlassian.stash.internal.sal.user;

import com.atlassian.fugue.Option;
import com.atlassian.sal.api.usersettings.UserSettingsBuilder;
import com.atlassian.sal.api.usersettings.UserSettingsService;
import com.atlassian.stash.internal.setting.MapSettingsBuilder;
import com.atlassian.bitbucket.setting.Settings;
import com.google.common.collect.Maps;

import javax.annotation.Nonnull;
import java.util.Map;
import java.util.Set;

import static com.google.common.base.Preconditions.checkArgument;

public class MapUserSettingsBuilder implements UserSettingsBuilder {

    private static final String KEY_TOO_LONG_MESSAGE_FORMAT =
            "Key {%s} is too long. Keys must be up to [%s] characters long";
    private static final String VALUE_TOO_LONG_MESSAGE_FORMAT =
            "Value {%s} is too long. Values must be up to [%s] characters long";
    private final Map<String, Object> settingsMap = Maps.newHashMap();

    @Nonnull
    @Override
    public MapUserSettingsBuilder put(@Nonnull String key, @Nonnull String value) {
        settingsMap.put(validateKey(key), validateValue(value));
        return this;
    }

    @Nonnull
    @Override
    public MapUserSettingsBuilder put(@Nonnull String key, boolean value) {
        settingsMap.put(validateKey(key), value);
        return this;
    }

    @Nonnull
    @Override
    public MapUserSettingsBuilder put(@Nonnull String key, long value) {
        settingsMap.put(validateKey(key), value);
        return this;
    }

    @Nonnull
    @Override
    public MapUserSettingsBuilder remove(@Nonnull String key) {
        settingsMap.remove(key);
        return this;
    }

    @Nonnull
    @Override
    public Option<Object> get(@Nonnull String key) {
        return Option.option(settingsMap.get(key));
    }

    @Nonnull
    @Override
    public Set<String> getKeys() {
        return settingsMap.keySet();
    }

    @Nonnull
    public MapUserSettingsBuilder putAll(@Nonnull DelegatingUserSettings userSettings) {
        // This method is used internally to copy existing values. We shouldn't need to worry about the
        // restriction imposed by the SAL API
        return putAll(userSettings.getSettings());
    }

    @Nonnull
    public MapUserSettingsBuilder putAll(@Nonnull Settings settings) {
        // This method is used internally to copy existing values. We shouldn't need to worry about the
        // restriction imposed by the SAL API
        settingsMap.putAll(settings.asMap());
        return this;
    }

    @Nonnull
    public DelegatingUserSettings build() {
        return new DelegatingUserSettings(new MapSettingsBuilder().addAll(settingsMap).build());
    }

    private String validateKey(String key) {
        // SAL API imposes these restriction on keys
        checkArgument(key != null, "Key cannot be null");
        checkArgument(key.length() <= UserSettingsService.MAX_KEY_LENGTH,
                KEY_TOO_LONG_MESSAGE_FORMAT, key, UserSettingsService.MAX_KEY_LENGTH);
        return key;
    }

    private String validateValue(String value) {
        // SAL API imposes these restriction on values
        checkArgument(value != null, "Value cannot be null");
        checkArgument(value.length() <= UserSettingsService.MAX_STRING_VALUE_LENGTH,
                VALUE_TOO_LONG_MESSAGE_FORMAT, value, UserSettingsService.MAX_KEY_LENGTH);
        return value;
    }
}
