package com.atlassian.bitbucket.scm;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;
import java.io.File;

/**
 * Provides a unifying abstraction for common functionality expected to be provided by both free-form
 * {@link CommandBuilder}s and, for those plugins which choose to provide them, type-safe builders.
 */
@NotThreadSafe
public interface CommandBuilderSupport<B extends CommandBuilderSupport<B>> {

    /**
     * Builds a {@link Command} which, when {@link Command#call() executed}, will provide output to the provided
     * {@code outputHandler} for processing.
     *
     * @param outputHandler the handler to receive and process output from the command as it executes
     * @return the built command
     * @throws NullPointerException if the provided {@code outputHandler} is {@code null}
     */
    @Nonnull
    <T> Command<T> build(@Nonnull CommandOutputHandler<T> outputHandler);

    /**
     * Clears any environment variables which have been set.
     *
     * @return {@code this}
     */
    @Nonnull
    B clearEnvironment();

    /**
     * Applies the default {@link CommandExitHandler}. The default exit handler used is based on the builder in play;
     * each builder is free to define its own default exit handler.
     * <p>
     * Note: If no explicit exit handler {@link #exitHandler(CommandExitHandler) is set}, the default will be applied
     * automatically when the command is {@link #build(CommandOutputHandler) built}; it is not possible to build a
     * command without an exit handler.
     *
     * @return {@code this}
     */
    @Nonnull
    B defaultExitHandler();

    /**
     * Applies the default working directory, which is the working directory for the current Java process. This may be
     * useful for commands that <i>create</i> directories, such as SCM clone commands.
     *
     * @return {@code this}
     */
    @Nonnull
    B defaultWorkingDirectory();

    /**
     * Adds an environment variable with the specified {@code name} and {@code value}. Both parameters are required,
     * and an exception will be thrown if either is not provided.
     *
     * @param name  the name of the environment variable to set
     * @param value the value to set for the environment variable
     * @return {@code this}
     * @throws IllegalArgumentException if {@code name} or {@code value} is empty or blank
     * @throws NullPointerException if {@code name} or {@code value} is {@code null}
     * @deprecated in 4.8 for removal in 5.0. Use {@link #withEnvironment(String, String)} instead.
     */
    @Deprecated
    @Nonnull
    B environment(@Nonnull String name, @Nonnull String value);

    /**
     * Sets the {@link CommandExitHandler} which should be invoked after the command exits. This handler receives:
     * <ul>
     *     <li>the exit code from the process</li>
     *     <li>any {@code Throwable} thrown during processing</li>
     *     <li>any output the process wrote to its error stream</li>
     * </ul>
     * Whether the command executes successfully or fails in some way (either in the command itself or in processing
     * performed by handlers attached to the command), the exit handler is <i>guaranteed</i> to be invoked.
     *
     * @param value the handler which should be invoked when the command exits
     * @return {@code this}
     * @throws NullPointerException if the provided {@code value} is {@code null}
     */
    @Nonnull
    B exitHandler(@Nonnull CommandExitHandler value);

    /**
     * Adds an environment variable with the specified {@code name} and {@code value}. Both parameters are required,
     * and an exception will be thrown if either is not provided.
     *
     * @param name  the name of the environment variable to set
     * @param value the value to set for the environment variable
     * @return {@code this}
     * @throws IllegalArgumentException if {@code name} or {@code value} is empty or blank
     * @throws NullPointerException if {@code name} or {@code value} is {@code null}
     * @since 4.8
     */
    @Nonnull
    B withEnvironment(@Nonnull String name, @Nonnull String value);

    /**
     * Sets the working directory which should be used when the command is run.
     *
     * @param value the desired working directory
     * @return {@code this}
     * @throws IllegalArgumentException if {@code value} references a nonexistent path, or a file
     * @throws NullPointerException if the provided {@code value} is {@code null}
     */
    @Nonnull
    B workingDirectory(@Nonnull File value);

    /**
     * Sets the working directory which should be used when the command is run. Paths provided here should be
     * <i>absolute</i>, to ensure the command runs in the correct directory.
     *
     * @param value the absolute path to the desired working directory
     * @return {@code this}
     * @throws IllegalArgumentException if {@code value} references a nonexistent path, or a file
     * @throws NullPointerException if the provided {@code value} is {@code null}
     */
    @Nonnull
    B workingDirectory(@Nonnull String value);
}
