package com.atlassian.bitbucket.internal.scm.git.lfs.http;

import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.internal.scm.git.lfs.GitLfsService;
import com.atlassian.bitbucket.repository.NoSuchRepositoryException;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.repository.RepositoryService;
import com.atlassian.bitbucket.scm.AuthenticationState;
import com.atlassian.bitbucket.scm.http.HttpRequestDetails;
import com.atlassian.bitbucket.scm.http.HttpScmRequest;
import com.atlassian.bitbucket.scm.http.HttpScmRequestHandler;

import javax.annotation.Nonnull;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Objects.requireNonNull;
import static java.util.Optional.empty;
import static java.util.Optional.of;

public class GitLfsHttpScmRequestHandler implements HttpScmRequestHandler {

    private static final String NAMESPACE = "/?((?<namespace>[^/']+)/)?";
    private static final String PROJECT = "(?<project>[^/]+)/";
    private static final String REPO = "(?<repo>[^/']+?)";
    private static final String EXT = "(?:\\.git)?";
    private static final Pattern SUPPORTS_URL_PATTERN = Pattern.compile(String.format(
            "%s%s%s%s/info/lfs/objects/batch", NAMESPACE, PROJECT, REPO, EXT));

    private final I18nService i18nService;
    private final GitLfsService lfsService;
    private final RepositoryService repositoryService;

    public GitLfsHttpScmRequestHandler(I18nService i18nService, GitLfsService lfsService,
                                       RepositoryService repositoryService) {
        this.i18nService = i18nService;
        this.lfsService = lfsService;
        this.repositoryService = repositoryService;
    }

    @Nonnull
    @Override
    public Optional<HttpScmRequest> create(@Nonnull HttpServletRequest request, @Nonnull HttpServletResponse response) {
        requireNonNull(request, "request");
        requireNonNull(response, "response");

        Matcher matcher = SUPPORTS_URL_PATTERN.matcher(request.getPathInfo());
        if (matcher.matches()) {
            String namespace = matcher.group("namespace");
            String projectKey = matcher.group("project");
            String repositorySlug = matcher.group("repo");
            Repository repository = getRepositoryOrThrow(namespace, projectKey, repositorySlug);

            return of(new GitLfsBatchScmRequest(request, response, i18nService, lfsService, repository));
        }
        return empty();
    }

    @Override
    public void sendAuthenticationError(@Nonnull AuthenticationState state, @Nonnull String summary,
                                        @Nonnull String detailMessage, @Nonnull HttpServletRequest request,
                                        @Nonnull HttpServletResponse response) throws IOException {
        HttpScmErrorUtils.sendError(response, 401, summary + "\n\n" + detailMessage);
    }

    @Override
    public void sendError(@Nonnull String summary, @Nonnull String detailMessage, @Nonnull HttpServletRequest request,
                          @Nonnull HttpServletResponse response) throws IOException {
        HttpScmErrorUtils.sendError(response, 404, summary + "\n\n" + detailMessage);
    }

    @Override
    public boolean supports(@Nonnull HttpRequestDetails requestDetails) {
        requireNonNull(requestDetails, "requestDetails");

        String pathInfo = requestDetails.getPathInfo();
        if ("POST".equals(requestDetails.getMethod()) && pathInfo != null) {
            Matcher matcher = SUPPORTS_URL_PATTERN.matcher(pathInfo);
            return matcher.matches();
        }
        return false;
    }

    private Repository getRepositoryOrThrow(String namespace, String projectKey, String repositorySlug) {
        Repository repository = repositoryService.getBySlug(namespace, projectKey, repositorySlug);
        if (repository == null) {
            throw new NoSuchRepositoryException(i18nService.createKeyedMessage(
                    "bitbucket.scm.git.lfs.nosuchrepo", projectKey, repositorySlug), null);
        }

        return repository;
    }
}
