package com.atlassian.bitbucket.mesh.execution;

import javax.annotation.Nonnull;
import java.util.concurrent.Callable;

public interface ExecutionContext {

    /**
     * Registers a cleanup task that is executed when the current execution ends. Note that the callback will be called
     * <em>once</em> when the current execution completes. If the current execution context has already been completed,
     * the callback is invoked immediately.
     *
     * @param callback callback that should be called when the execution completes.
     */
    void addCleanupCallback(@Nonnull Runnable callback);

    /**
     * Executes the provided {@code callable} with this context set as the current execution context.
     *
     * @param callable the {@link Callable} to execute
     * @param <V> the type of the value computed by the {@code callable}
     * @return the value returned by the provided {@code callable}
     * @throws Exception if there's an exception while executing the {@code callable}
     */
    <V> V call(@Nonnull Callable<V> callable) throws Exception;

    /**
     * Returns the reasonably unique ID of this {@link ExecutionContext}, which consists of:
     * <ol>
     * <li>The reasonably unique identifier of this Mesh instance</li>
     * <li>The minute of the current day</li>
     * <li>The number of requests, including the current one, which have been serviced by the application since it
     * was started</li>
     * <li>The number of requests which were being processed concurrently at the time the ID was generated</li>
     * </ol>
     * These fields are separated by an "x". The hour of the day portion resets each night, and the concurrency count
     * rises and falls with the load on the server. The request count is monotonically increasing until the {@code long}
     * for the counter wraps (which at 1,000 requests per second will take 252 million years).
     * <p>
     * It is worth noting that the uniqueness period required for request IDs is 1 day, which is the period at which
     * log files are rotated. The goal of this ID is not to be universally unique so much as it is to allow support to
     * easily trace the logging related to a single request, within the log files.
     *
     * @return the identifier of the execution context
     */
    @Nonnull
    String getId();

    /**
     * Executes the provided {@code runnable} with this context set as the current execution context.
     *
     * @param runnable the {@link Runnable} to run
     */
    void run(@Nonnull Runnable runnable);

    /**
     * Wraps a {@link Callable} so that it executes with this context as the
     * {@link ExecutionManager#getContext() current} execution context.
     *
     * @param callable the {@link Callable} to be executed
     * @param <V> the type computed by the callable after execution
     * @return the provided {@code callable}
     */
    @Nonnull
    <V> Callable<V> wrap(@Nonnull Callable<V> callable);

    /**
     * Wraps a {@link Runnable} so that it executes with this context as the
     * {@link ExecutionManager#getContext() current} execution context.
     *
     * @param runnable the {@link Runnable} to wrap
     * @return the wrapped {@link Runnable}
     */
    @Nonnull
    Runnable wrap(@Nonnull Runnable runnable);

    /**
     * Wraps {@code target} with a proxy so that it executes with this context as the
     * {@link ExecutionManager#getContext() current} execution context.
     *
     * @param target the target to wrap
     * @param <T> the type of the target
     * @param interfaces the interfaces the generated proxy should implement
     * @return a proxy that sets up this {@link ExecutionContext} around each method invocation on the proxy
     */
    @Nonnull
    <T> T wrap(@Nonnull T target, @Nonnull Class<?>... interfaces);
}