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

import com.atlassian.bitbucket.internal.scm.git.lfs.settings.GitLfsSettingsService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.jwt.CanonicalHttpRequest;
import com.atlassian.jwt.SigningAlgorithm;
import com.atlassian.jwt.core.writer.JwtClaimsBuilder;
import com.atlassian.jwt.writer.JwtJsonBuilder;
import com.atlassian.jwt.writer.JwtJsonBuilderFactory;
import com.atlassian.jwt.writer.JwtWriterFactory;

import javax.annotation.Nonnull;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;

import static com.atlassian.bitbucket.internal.scm.git.lfs.GitLfsConstants.PLUGIN_KEY;
import static java.util.Objects.requireNonNull;

/**
 * A helper for building JSON Web Tokens (JWT).
 *
 * @see <a href="https://tools.ietf.org/html/rfc7519">JSON Web Token (JWT) RFC</a>
 */
public class GitLfsJwtHelper {

    private final JwtJsonBuilderFactory jwtJsonBuilderFactory;
    private final JwtWriterFactory jwtWriterFactory;
    private final GitLfsSettingsService settingsService;

    public GitLfsJwtHelper(JwtJsonBuilderFactory jwtJsonBuilderFactory, JwtWriterFactory jwtWriterFactory,
                           GitLfsSettingsService settingsService) {
        this.jwtJsonBuilderFactory = jwtJsonBuilderFactory;
        this.jwtWriterFactory = jwtWriterFactory;
        this.settingsService = settingsService;
    }

    @Nonnull
    public String buildToken(@Nonnull CanonicalHttpRequest httpRequest, @Nonnull Repository repository, long expiry) {
        requireNonNull(httpRequest, "httpRequest");
        requireNonNull(repository, "repository");

        long iat = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
        long exp = iat + expiry;

        JwtJsonBuilder builder = jwtJsonBuilderFactory.jsonBuilder()
                .claim("repo", repository.getId())
                .expirationTime(exp)
                .issuedAt(iat)
                .issuer(PLUGIN_KEY);
        try {
            JwtClaimsBuilder.appendHttpRequestClaims(builder, httpRequest);
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            throw new JwtTokenGenerationException("Failed to append \"qsh\" JWT claim.", e);
        }

        //The leading "JWT " makes this available for validation by the JwtAuthFilter
        return "JWT " + jwtWriterFactory.macSigningWriter(SigningAlgorithm.HS256, settingsService.getSharedSecret())
                .jsonToJwt(builder.build());
    }
}
