package com.atlassian.bitbucket.timezone;

import com.atlassian.bitbucket.AuthorisationException;
import com.atlassian.bitbucket.user.ApplicationUser;

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

/**
 * Provides access to the time zones used in Bitbucket. There are three types of time zones:
 *
 * <dl>
 *     <dt>JVM time zone</dt>
 *     <dd>
 *         The time zone configured using the system property {@code user.timezone}. When not explicitly set the
 *         the JVM inherits the the time zone from the launching user. This zone is used for most non-display
 *         related tasks, such as log timestamps.
 *         It is also the default time zone when no other can be found. See {@link #getJvmTimeZone()}.
 *     </dd>
 *     <dt>Server time zone</dt>
 *     <dd>
 *         The default time zone for Bitbucket. It can be changed at runtime by an administrator. It
 *         is possible for this time zone to not be set. The server time zone is the fallback time zone when a user
 *         has no explicit user time zone defined. See {@link #setServerTimeZone(ZoneId)} and
 *         {@link #getServerTimeZone()}.
 *     </dd>
 *     <dt>User time zone</dt>
 *     <dd>
 *         The time zone as configured by a user. It can be changed at runtime by the user. It is possible for this
 *         time zone to not be set.
 *     </dd>
 * </dl>
 *
 * <p>
 *     Given a user the lookup for a time zone returns the first configured time zone of user, server and JVM
 *     in that order. There will always be a JVM time zone to return if all else fails. See
 *     {@link UserTimeZoneSupplier#getTimeZone()} and {@link UserTimeZoneSupplier#getTimeZone(ApplicationUser)}.
 * </p>
 *
 * @see UserTimeZoneSupplier UserTimeZoneSupplier
 * @since 4.6
 */
public interface TimeZoneService extends UserTimeZoneSupplier {

    /**
     * @return the time zone that will be used if a user has no explicit time zone. Always returns a time zone by falling
     * back to {@link #getJvmTimeZone()} if necessary.
     */
    @Nonnull
    ZoneId getDefaultTimeZone();

    /**
     * @return the time zone configured for the JVM.
     */
    @Nonnull
    ZoneId getJvmTimeZone();

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

    /**
     * Return the time zone the current user has explicitly configured. This method will return {@link Optional#empty()}
     * when no time zone is configured.
     *
     * <p>
     *     NOTE: Use {@link #getTimeZone()} when looking up the time zone that is applicable to the current user (e.g.
     *     when rendering dates). The {@code getTimeZone} method will always return a time zone by falling back to a
     *     set of defaults.
     * </p>
     *
     * @return the current user's explicitly configured time zone or {@link Optional#empty()} if there is none.
     */
    @Nonnull
    Optional<ZoneId> getUserTimeZone();

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

    /**
     * Set the time zone of the current user. A user is always able to change its own time zone.
     *
     * @param zone the time zone to set for the current user. {@code null} may be passed
     *             to indicate the user has no configured time zone.
     * @throws AuthorisationException if called anonymously
     * @throws IllegalUserStateException if the context user type does not support setting a time zone.
     */
    void setUserTimeZone(@Nullable ZoneId zone);
}
