package com.atlassian.bitbucket.server;

import com.atlassian.bitbucket.mail.MailHostConfiguration;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.timezone.TimeZoneService;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.net.URI;
import java.util.Date;
import java.util.TimeZone;

/**
 * A service for retrieving and updating application properties.
 */
public interface ApplicationPropertiesService extends ApplicationModeSupplier {

    /**
     * Gets the base URL of the current instance.
     * <p>
     * The URL is guaranteed to be without a trailing slash
     *
     * @return base URL of the current instance, without trailing slash or {@code null} if the base URL is not configured
     */
    @Nullable
    URI getBaseUrl();

    /**
     * Gets the display name of the current instance.
     *
     * @return name of the current instance or {@code null} if the display name is not configured
     */
    @Nullable
    String getDisplayName();

    /**
     * Sets the base URL of the current instance.
     * <p>
     * The URL will be persisted without a trailing slash, if present.
     * Ex: given {@code http://server/stash/}, the property will be saved as {@code http://server/stash}
     *
     * @param baseUrl publicly accessible URL for the current instance
     */
    void setBaseURL(@Nonnull URI baseUrl);

    /**
     * Gets the login page URI for the current instance.
     *
     * @param redirectUri the URI the user should be redirected to after a successful login.
     * @return the login page URI for the current instance.
     */
    @Nonnull
    URI getLoginUri(URI redirectUri);

    /**
     * @return the build version of the current instance.
     */
    @Nonnull
    String getBuildVersion();

    /**
     * Sets the display name of the current instance.
     *
     * @param name display name for the current instance
     */
    void setDisplayName(@Nonnull String name);

    /**
     * @return the build timestamp of the current instance.
     */
    @Nonnull
    Date getBuildTimestamp();

    /**
     * @return the build number of the current instance.
     */
    @Nonnull
    String getBuildNumber();

    /**
     * @return the commit hash of the version of the current instance
     */
    @Nonnull
    String getCommitHash();

    /**
     * @return the directory inside {@code stash.home} that contains any scripts / executables used by the application.
     */
    @Nonnull
    File getBinDir();

    /**
     * @return the directory inside {@code stash.home} that contains any caches used by the application.
     */
    @Nonnull
    File getCacheDir();

    /**
     * @return the directory inside {@code stash.home} that contains the configuration files.
     */
    @Nonnull
    File getConfigDir();

    /**
     * @return the directory inside {@code stash.home} that contains data files, such as repositories
     */
    @Nonnull
    File getDataDir();

    /**
     * Returns the current home directory for this instance. The home directory will contain only node-local data,
     * such as caches, backups, logs and tmp. When running standalone this directory will also generally contain
     * the shared home under {@code /shared}, but it is not <i>required</i> to.
     *
     * @return the home directory for the instance.
     */
    @Nonnull
    File getHomeDir();

    /**
     * @return the name of the JDBC driver class currently used to connect to the database
     */
    @Nonnull
    String getJdbcDriver();

    /**
     * @return the version of the JDBC driver currently in use, in a database-specific format.
     *         If the version cannot be determined {@code "Unknown"} is returned.
     */
    @Nonnull
    String getJdbcDriverVersion();

    /**
     * @return the JDBC URL for the database currently in use
     */
    @Nonnull
    String getJdbcUrl();

    /**
     * @return the directory inside {@code stash.shared.home} that contains the repositories.
     */
    @Nonnull
    File getRepositoriesDir();

    /**
     * @param repository the repository
     * @return the directory containing the hosted repository.
     */
    @Nonnull
    File getRepositoryDir(@Nonnull Repository repository);

    /**
     * Returns the shared home directory. This directory contains repository data, config and plugins/installed-plugins.
     * When the application is clustered, this directory is shared among all nodes.
     *
     * @return the shared home directory for the instance
     */
    @Nonnull
    File getSharedHomeDir();

    /**
     * @return the default time zone for the server.
     * @deprecated since 4.6 for removal in 5.0. Use {@link TimeZoneService#getDefaultTimeZone()}.
     */
    @Deprecated
    @Nonnull
    TimeZone getDefaultTimeZone();

    /**
     * @return the number of failed login attempts before the user is required to enter a CAPTCHA verification.
     */
    int getMaxCaptchaAttempts();

    /**
     * Sets the number of failed login attempts before the user is required to enter a CAPTCHA verification.
     *
     * @param maxCaptchaAttempts maximum number of failed CAPTCHA
     */
    void setMaxCaptchaAttempts(int maxCaptchaAttempts);

    /**
     * Gets the email address used in the 'sender' field for any email notification.
     *
     * @return sender address for email notifications
     */
    @Nullable
    String getServerEmailAddress();

    /**
     * Sets the email address used in the 'sender' field for any email notification.
     *
     * @param emailAddress sender address for email notifications
     */
    void setServerEmailAddress(String emailAddress);

    /**
     * Retrieves the server ID used for licensing.
     *
     * @return the server ID
     */
    @Nullable
    String getServerId();

    /**
     * @return the temp directory for the instance
     */
    @Nonnull
    File getTempDir();

    /**
     * @return whether public signup is allowed for external users.
     */
    boolean isAllowPublicSignUp();

    /**
     * Sets whether public signup is allowed for external users.
     *
     * @param publicSignUp whether to allow public signup
     */
    void setAllowPublicSignUp(boolean publicSignUp);

    /**
     * @return whether disaster recovery tasks should be run on startup.
     * @since 4.8
     */
    boolean isDisasterRecovery();

    /**
     * @return whether debug logging is enabled.
     */
    boolean isDebugLoggingEnabled();

    /**
     * @return whether profiling is enabled.
     */
    boolean isProfilingEnabled();

    /**
     * @return whether the SCM requests are allowed over HTTP(S).
     */
    boolean isHttpScmHostingEnabled();

    /**
     * @return whether JMX is enabled
     *
     * @since 4.8
     */
    boolean isJmxEnabled();

    /**
     * @return whether CAPTCHA is required for signing up.
     */
    boolean isShowCaptchaOnSignUp();

    /**
     * Sets whether the SCM requests are allowed over HTTP(S).
     *
     * @param enabled whether to enable HTTP(S) access
     */
    void setHttpScmHostingEnabled(boolean enabled);

    /**
     * Sets whether a CAPTCHA verification is required for signing up.
     *
     * @param captchaOnSignUp whether to enable CAPTCHA on signup
     */
    void setShowCaptchaOnSignUp(boolean captchaOnSignUp);

    /**
     * @return whether or not the application has completed setup
     */
    boolean isSetup();

    /**
     * @return the {@link ApplicationMode mode} of this instance
     */
    @Override
    @Nonnull
    ApplicationMode getMode();

    /**
     * Retrieves the mail host configuration
     *
     * @return the mail host configuration, {@code null} if undefined.
     */
    @Nullable
    MailHostConfiguration getMailHostConfiguration();

    /**
     * Sets the mail host configuration, replacing any existing configuration.
     *
     * @param mailHostConfiguration the configuration to store
     */
    void setMailHostConfiguration(@Nonnull MailHostConfiguration mailHostConfiguration);

    /**
     * Remove the mail host configuration.
     */
    void deleteMailHostConfiguration();

    /**
     * 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.
     * <p>
     * This method should be used only by plugins.
     *
     * @param propertyName name of the property, which must start with "plugin."
     * @return property value or null if not defined.
     * @throws IllegalArgumentException if the property name is null,
     *                                  or if the property name does not start with "plugin."
     */
    @Nullable
    String getPluginProperty(@Nonnull String propertyName);

    /**
     * See {@link #getPluginProperty}
     *
     * @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 getPluginProperty(@Nonnull String propertyName, @Nullable String defaultValue);

    /**
     * See {@link #getPluginProperty}
     *
     * @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 getPluginProperty(@Nonnull String propertyName, int defaultValue) throws NumberFormatException;

    /**
     * See {@link #getPluginProperty}
     *
     * @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 getPluginProperty(@Nonnull String propertyName, long defaultValue) throws NumberFormatException;

    /**
     * See {@link #getPluginProperty}
     *
     * @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 getPluginProperty(@Nonnull String propertyName, boolean defaultValue);

    /**
     * See {@link #getPluginProperty}
     *
     * @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.
     */
    double getPluginProperty(@Nonnull String propertyName, double defaultValue) throws NumberFormatException;
}
