package com.atlassian.bitbucket.scm.annotation;

import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.context.RepoTestExecutionListener;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotates a field on a test class into which a {@link Repository Repository} should
 * be injected.
 * <p>
 * To properly apply this annotation to a field:
 * <ul>
 *     <li>The field's type must be {@code Repository}</li>
 *     <li>The zipped repository identified by {@link #value()} must match the {@link #scmId() SCM}</li>
 * </ul>
 * Default repositories for supported SCMs will be made available by the underlying testing infrastructure, and the
 * repository unpacked will be correct for the type when using the default. When configuring an explicit value for the
 * repository, it is left to the developer to ensure it matches the {@link #scmId() SCM}.
 * <p>
 * Annotated fields will be injected with a constructed {@code Repository} (<i>not a mock!</i>) which will have all of
 * its properties set in a valid way and an ID which corresponds to the location where the data was unpacked. Tests may
 * then use the methods on {@link ApplicationPropertiesService ApplicationPropertiesService}
 * for locating and interacting with the data.
 * <p>
 * This annotation is intended as a simplification for building unit tests for SCM commands which operate on supported
 * SCM systems. The underlying support classes automatically manage unpacking, caching and cleanup to ensure the tests
 * run as quickly as possible in terms of their setup and teardown. Rather than writing redundant initialization code,
 * be it in each test, in a helper method or in an {@code &#064;Before}-annotated method, just annotate the field.
 *
 * @see DirtiesRepo
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Repo {

    /**
     * Controls the injected repository's {@link Repository#getScmId() SCM ID}.
     * <p>
     * Note: The SCM chosen for "default", which is the default value, is configured when wiring the
     * {@link RepoTestExecutionListener RepoTestExecutionListener}.
     *
     * @return the SCM for the repository, or "default" to use a configured default SCM
     */
    String scmId() default "default";

    /**
     * Defines the path to the zip file containing the repository. The specified path is expected to be a valid
     * Spring {@code Resource} path. The test {@code ApplicationContext} will be used to resolve the value to a
     * {@code Resource} of the correct type.
     * <p>
     * Note: Checking repositories directly into the codebase results in bloat across versions, and is better avoided.
     *
     * @return the resource path to to the repository, or {@code "default"} to unpack the configured default
     *         repository for the {@link #scmId() SCM type}
     */
    String value() default "default";
}
