package com.atlassian.bitbucket.project;

import com.atlassian.bitbucket.permission.PermissionService;
import com.atlassian.bitbucket.validation.annotation.OptionalString;
import com.atlassian.bitbucket.validation.annotation.RequiredString;
import com.atlassian.bitbucket.validation.groups.Create;
import com.atlassian.bitbucket.validation.groups.TrustedCreate;
import com.atlassian.bitbucket.validation.groups.Update;
import com.atlassian.bitbucket.validation.groups.TrustedUpdate;

import javax.annotation.Nonnull;
import javax.validation.constraints.Pattern;

/**
 * Describes a project in the system.
 */
public interface Project {

    /**
     * The regexp to validate the project key when creating or updating from an untrusted source.
     */
    String KEY_REGEXP = "[a-zA-Z][a-zA-Z0-9_\\-]*";

    /**
     * The regexp to validate the project key when creating or updating from a trusted source - slightly more relaxed
     * than {@link #KEY_REGEXP} by allowing project keys that start with a tilda as do personal projects.
     */
    String KEY_TRUSTED_REGEXP = "~?" +  KEY_REGEXP;

    /**
     * The maximum length of a project key.
     */
    int MAX_KEY_LENGTH = 128;

    /**
     * The maximum length of a project namespace.
     *
     * @since 4.2
     */
    int MAX_NAMESPACE_LENGTH = 128;

    /**
     * The regexp to validate the project name.
     *
     * @since 4.4
     */
    String NAME_REGEXP = "^[^~].*";

    /**
     * The regexp to validate the project namespace.
     *
     * @since 4.2
     */
    String NAMESPACE_REGEXP = "[a-zA-Z0-9_\\-]*";

    /**
     *
     * @param visitor the visitor to process the project instance
     */
    <T> T accept(@Nonnull ProjectVisitor<T> visitor);

    @OptionalString(size = 255)
    String getDescription();

    int getId();

    /**
     * Retrieves a flag indicating whether this project is public.
     * <p>
     * Note, this flag is taken into account when calculating whether this project is accessible to
     * unauthenticated users but is <i>not</i> the definitive answer. For a definitive answer, use
     * {@link PermissionService#isPubliclyAccessible(Project) isPubliclyAccessible}.
     *
     * @return {@code true} if the project has been marked as public, {@code false} otherwise
     */
    boolean isPublic();

    @Pattern.List({
        @Pattern(regexp = KEY_REGEXP, groups = {Create.class, Update.class},
                message = "{com.atlassian.bitbucket.validation.project.key.pattern.message}"),
        @Pattern(regexp = KEY_TRUSTED_REGEXP, groups = {TrustedCreate.class, TrustedUpdate.class},
                message = "{com.atlassian.bitbucket.validation.project.key.trusted.pattern.message}")
    })
    @RequiredString(size = MAX_KEY_LENGTH)
    String getKey();

    @Pattern(regexp = NAME_REGEXP, groups = {Create.class, Update.class},
            message = "{com.atlassian.bitbucket.validation.project.name.pattern.message}")
    @RequiredString(size = MAX_KEY_LENGTH)
    String getName();

    /**
     * @return the project's namespace. Namespaces are only supported in
     *         {@link com.atlassian.bitbucket.server.ApplicationMode#MIRROR mirror mode} and will be {@code null}
     *         in standard installations.
     * @since 4.2
     */
    @Pattern(regexp = NAMESPACE_REGEXP, message = "{com.atlassian.bitbucket.validation.project.namespace.pattern.message}")
    @OptionalString(size = MAX_NAMESPACE_LENGTH)
    String getNamespace();

    /**
     * Retrieves the project's {@link ProjectType type}. See the documentation for the various types to determine what
     * interfaces each implements.
     *
     * @return the {@link ProjectType type} for this project
     */
    @Nonnull
    ProjectType getType();
}
