package com.atlassian.stash.internal.server;

import com.atlassian.bitbucket.server.ApplicationMode;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.bitbucket.server.ApplicationState;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.ZoneId;
import java.util.Locale;
import java.util.Optional;

/**
 * Non-public extensions to the {@link ApplicationPropertiesService} which are not intended to be available to plugins.
 */
public interface InternalApplicationPropertiesService extends ApplicationPropertiesService {

    /**
     * Retrieves the identifier of the configured {@link com.atlassian.stash.internal.avatar.AvatarSource AvatarSource}.
     *
     * @return the configured avatar source, which may be {@code null} if one has not been configured explicitly
     */
    String getAvatarSource();

    /**
     * Retrieves the time that the license user count was first exceeded.
     *
     * @return the time of the first license exceeded failure in milliseconds, or {@link Optional#empty()} if the grace
     * period has never been started.
     * @since 4.8
     */
    @Nonnull
    Optional<Long> getGracePeriodStartTime();

    /**
     * Retrieves the <i>last known</i> {@link ApplicationMode application mode}. When the application is started, the
     * currently specified application mode is compared to the last know mode to prevent switching between modes.
     * <p>
     * Note: The application mode can be selected once and cannot be changed after that. If the application mode does
     * change, the application will be prevented from starting up.
     *
     * @since 4.1
     * @return the application mode from the last system run
     */
    @Nonnull
    Optional<ApplicationMode> getLastMode();

    /**
     * Retrieves the <i>last known</i> shared home directory. Each time the application is started, its current home
     * directory is tested against its previous one to allow detecting when {@code STASH_SHARED_HOME} is moved.
     *
     * @return the shared home directory from the last system run
     * @since 3.2
     */
    @Nullable
    String getLastSharedHomeDir();

    /**
     * @return the default {@link Locale locale} for Stash.
     */
    @Nonnull
    Locale getLocale();

    /**
     * Return a value of a property as defined in the applications configuration files.
     * Code usually should use the Spring @Value annotation to get these injected, but in
     * cases where that is not possible or the property name must be dynamically determined,
     * this method and its adjuncts can be used instead.
     *
     * @param propertyName name of the property
     * @return property value or null if not defined.
     * @throws IllegalArgumentException if the property name is null
     */
    @Nullable
    String getProperty(@Nonnull String propertyName);

    /**
     * See {@link #getProperty}
     * @param propertyName Name of the property
     * @param defaultValue The default value to return if it is not present
     * @return property value or defaultValue if not defined.
     */
    @Nullable
    String getProperty(@Nonnull String propertyName, @Nullable String defaultValue);

    /**
     * See {@link #getProperty}
     * @param propertyName Name of the property
     * @param defaultValue The default value to return if it is not present
     * @return property value or defaultValue if not defined.
     */
    int getProperty(@Nonnull String propertyName, int defaultValue) throws NumberFormatException;

    /**
     * See {@link #getProperty}
     * @param propertyName Name of the property
     * @param defaultValue The default value to return if it is not present
     * @return property value or defaultValue if not defined.
     */
    long getProperty(@Nonnull String propertyName, long defaultValue) throws NumberFormatException;

    /**
     * See {@link #getProperty}
     * @param propertyName Name of the property
     * @param defaultValue The default value to return if it is not present
     * @return property value or defaultValue if not defined.
     *
     * @since 4.1
     */
    double getProperty(@Nonnull String propertyName, double defaultValue) throws NumberFormatException;

    /**
     * See {@link #getProperty}
     * @param propertyName Name of the property
     * @param defaultValue The default value to return if it is not present
     * @return property value or defaultValue if not defined.
     */
    boolean getProperty(@Nonnull String propertyName, boolean defaultValue);

    /**
     * Return the configured server timezone. {@link Optional#empty()} will be returned if no server timezone is
     * configured.
     *
     * @return the configured server timezone.
     * @since 4.6
     */
    @Nonnull
    Optional<ZoneId> getServerTimeZone();

    /**
     * Set the instance's application mode
     *
     * @param mode the instance's application mode
     */
    void setMode(@Nonnull ApplicationMode mode);

    /**
     * Sets the identifier of the configured {@link com.atlassian.stash.internal.avatar.AvatarSource AvatarSource}, which
     * will be used on subsequent requests (and by default on subsequent executions) to provide avatar URLs.
     *
     * @param avatarSource the new avatar source, which may be {@code null} to use the default
     */
    void setAvatarSource(String avatarSource);

    /**
     * Sets whether DEBUG logging should be enabled. Note that setting this property will NOT enable/disable DEBUG
     * logging. It will only persist the setting.
     *
     * @param debugLoggingEnabled whether debug logging should be enabled.
     */
    void setDebugLoggingEnabled(boolean debugLoggingEnabled);

    /**
     * Set the time that the license user count was first exceeded.
     *
     * @param time the time that the license was first exceeded in milliseconds, or {@code null} to reset the grace
     *             period
     * @since 4.8
     */
    void setGracePeriodStartTime(@Nullable Long time);

    /**
     * Stores the <i>current</i> shared home directory as the {@link #getLastSharedHomeDir() last known home directory}.
     * This should only be called after detecting {@code STASH_SHARED_HOME} has been moved between restarts.
     *
     * @param lastSharedHomeDir the <i>current</i> shared home directory, to store as the last known home directory
     * @throws IllegalArgumentException if the provided {@code lastSharedHomeDir} is blank
     * @throws NullPointerException if the provided {@code lastSharedHomeDir} is {@code null}
     */
    void setLastSharedHomeDir(@Nonnull String lastSharedHomeDir);

    /**
     * Sets the default {@link Locale locale} of Stash, where a {@code null} value will revert back to the environmental
     * JVM setting.
     *
     * @param locale the {@link Locale locale}
     */
    void setLocale(@Nullable Locale locale);

    /**
     * Sets whether profiling should be enabled. Setting this property will NOT enable/disable profiling. It will only
     * persist the setting.
     *
     * @param profilingEnabled whether profiling should be enabled.
     */
    void setProfilingEnabled(boolean profilingEnabled);

    /**
     * Sets the server ID.
     *
     * @param serverId the server ID
     */
    void setServerId(String serverId);

    /**
     * Set the server timezone. {@code null} may be passed to clear the setting.
     *
     * @param zone the zone of configure or {@code null} to clear the setting.
     * @since 4.6
     */
    void setServerTimeZone(@Nullable ZoneId zone);

    /**
     * Set whether the application has completed the setup wizard
     *
     * @param setup whether or not the application has completed setup
     * @see ApplicationState
     */
    void setSetup(boolean setup);

    /**
     * @return whether or not the application has bootstrapped
     * @since 3.8
     */
    boolean isBootstrapped();
}
