package com.atlassian.stash.internal.plugin.readme.rest;

import com.atlassian.bitbucket.ServerException;
import com.atlassian.bitbucket.content.ContentService;
import com.atlassian.bitbucket.content.NoSuchPathException;
import com.atlassian.bitbucket.repository.RefService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.rest.NotFoundException;
import com.atlassian.bitbucket.rest.util.ResourcePatterns;
import com.atlassian.bitbucket.rest.util.ResponseFactory;
import com.atlassian.bitbucket.rest.util.RestUtils;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.markup.MarkupFactory;
import com.atlassian.markup.MarkupParser;
import com.atlassian.plugins.rest.common.security.AnonymousAllowed;
import com.atlassian.stash.internal.plugin.readme.ContentUtil;
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 static com.atlassian.bitbucket.rest.util.CachePolicies.getCacheControlObjectId;
import static com.atlassian.bitbucket.rest.util.RestUtils.getOrDefaultBranch;
import static com.atlassian.stash.internal.plugin.readme.MaxSizeByteArrayOutputStream.MaxBytesExceededException;

/**
 * Handles converting repository markup content to HTML.
 *
 * @since 1.3
 */
@Consumes({MediaType.APPLICATION_JSON})
@Path(ResourcePatterns.REPOSITORY_URI + "/markup")
@Singleton
@AnonymousAllowed
public class ReadmeResource {

    private static final String PROP_MAX_SIZE = "plugin.bitbucket-readme.max.size";
    /** Default maximum allowed byte size of the readme files. Default to 1 MiB */
    private static final int DEFAULT_MAX_SIZE = 1024 * 1024;

    private final ContentService contentService;
    private final RefService refService;
    private final MarkupFactory markupFactory;
    private final int maxSize;

    public ReadmeResource(ContentService contentService, MarkupFactory markupFactory, RefService refService,
                          ApplicationPropertiesService propertiesService) {
        this.contentService = contentService;
        this.markupFactory = markupFactory;
        this.refService = refService;

        maxSize = propertiesService.getPluginProperty(PROP_MAX_SIZE, DEFAULT_MAX_SIZE);
    }

    /**
     * Serves the html of a given markup file in the repository, possibly at a specific revision.
     *
     * @param repository the repository containing the file to be rendered
     * @param path       the path of a markup file in the repository
     * @param at         the commit to retrieve file changes for a page of changes, defaulting to the default branch
     * @param blob       the blob id of a file; a query param purely used for browser caching purposes
     * @return the html of the markup file
     */
    @GET
    @Path("{path:.*}")
    @Produces({RestUtils.TEXT_HTML_UTF8})
    public Response getContent(@Context Repository repository,
                               @PathParam("path") @DefaultValue("") String path,
                               @QueryParam("at") @DefaultValue("") String at,
                               @QueryParam("blob") @DefaultValue("") String blob) {
        String objectId = getOrDefaultBranch(refService, repository, at);
        MarkupParser parser = markupFactory.getParserUnsafe(displayDefaultReadmeAsText(path.trim()));
        if (parser == null) {
            return Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE).build();
        }
        try {
            String content = ContentUtil.getFileContentAsString(contentService, repository, objectId, path, maxSize);
            String html = parser.markup(content);
            return ResponseFactory.ok(html).cacheControl(getCacheControlObjectId(blob)).build();
        } catch (NoSuchPathException e) {
            throw new NotFoundException(e.getMessage());
        } catch (ServerException e) {
            if (e.getCause() instanceof MaxBytesExceededException) {
                return Response.status(413).build();
            }
            throw e;
        }
    }

    private String displayDefaultReadmeAsText(String path) {
        if (path.equalsIgnoreCase("readme")) {
            return "readme.txt";
        } else {
            return path;
        }
    }
}
