package com.atlassian.stash.internal.rest.content;

import com.atlassian.bitbucket.rest.BadRequestException;
import com.atlassian.bitbucket.rest.RestErrors;
import com.atlassian.bitbucket.rest.util.*;
import com.atlassian.plugins.rest.common.security.AnonymousAllowed;
import com.atlassian.bitbucket.content.ContentService;
import com.atlassian.bitbucket.content.ContentTreeNode;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.repository.RefService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.util.PageRequest;
import com.sun.jersey.spi.resource.Singleton;

import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;

import static com.atlassian.bitbucket.rest.util.CachePolicies.getCacheControlObjectId;
import static com.atlassian.bitbucket.rest.util.RestDocUtils.pageOf;

@AnonymousAllowed
@Consumes({MediaType.APPLICATION_JSON})
@Path(ResourcePatterns.REPOSITORY_URI + "/files")
@Produces({RestUtils.APPLICATION_JSON_UTF8})
@Singleton
public class FileListResource {

    private final ContentService contentService;
    private final I18nService i18nService;
    private final RefService refService;

    public FileListResource(ContentService contentService, I18nService i18nService,
                            RefService refService) {
        this.contentService = contentService;
        this.i18nService = i18nService;
        this.refService = refService;
    }

    /**
     * Retrieve a page of files from particular directory of a repository. The search is done recursively, so all files
     * from any sub-directory of the specified directory will be returned.
     * <p>
     * The authenticated user must have <strong>REPO_READ</strong> permission for the specified repository to call this
     * resource.
     *
     * @param path the directory to list files for.
     * @param at   the commit ID or ref (e.g. a branch or tag) to list the files at.
     *             If not specified the default branch will be used instead.
     * @response.representation.200.mediaType application/json
     * @response.representation.200.qname page
     * @response.representation.200.example {@link FileListResource#EXAMPLE}
     * @response.representation.200.doc A page of files.
     *
     * @response.representation.400.mediaType application/json
     * @response.representation.400.qname errors
     * @response.representation.400.example {@link RestErrors#EXAMPLE}
     * @response.representation.400.doc The path requested is not a directory at the supplied commit.
     *
     * @response.representation.401.mediaType application/json
     * @response.representation.401.qname errors
     * @response.representation.401.example {@link RestErrors#EXAMPLE}
     * @response.representation.401.doc The currently authenticated user has insufficient permissions
     *                                  to view the repository.
     *
     * @response.representation.404.mediaType application/json
     * @response.representation.404.qname errors
     * @response.representation.404.example {@link RestErrors#EXAMPLE}
     * @response.representation.404.doc The path requested does not exist at the supplied commit.
     */
    @GET
    @Path("{path:.*}")
    public Response listRepositoryFiles(@Context final Repository repository,
                                        @PathParam("path") @DefaultValue("") final String path,
                                        @QueryParam("at") final String at,
                                        @Context final PageRequest pageRequest) {
        final String objectId = RestUtils.getOrDefaultBranch(refService, repository, at);
        if (contentService.getType(repository, objectId, path) != ContentTreeNode.Type.DIRECTORY) {
            throw new BadRequestException(i18nService.getMessage("bitbucket.rest.filelist.not.directory", path, at));
        }

        return ResponseFactory.ok(new JsonStreamingOutput() {

            @Override
            public void write(StatefulJsonWriter writer) throws IOException, WebApplicationException {
                contentService.streamDirectory(repository, objectId, path, true,
                        new JsonContentTreeCallback(writer), pageRequest);
            }
        }, getCacheControlObjectId(at)).build();
    }

    // This method is intentionally undocumented. The REST doclet generator takes the docs from the
    // first method for overloaded methods and applies it to both.
    @GET
    public Response listRepositoryFiles(@Context Repository repository,
                                        @QueryParam("at") @DefaultValue("") String at,
                                        @Context PageRequest pageRequest) {
        return listRepositoryFiles(repository, "", at, pageRequest);
    }

    public static final RestPage<String> EXAMPLE = pageOf("path/to/file.txt");
}
