package com.atlassian.bitbucket.internal.mirroring.jwt;

import com.atlassian.jwt.SigningAlgorithm;
import com.atlassian.jwt.core.writer.JwtClaimsBuilder;
import com.atlassian.jwt.httpclient.CanonicalHttpUriRequest;
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 java.util.Objects.requireNonNull;

/**
 * A helper to generate JWT tokens for arbitrary URLs.
 */
public class JwtTokenHelper {

    private final JwtJsonBuilderFactory jwtJsonBuilderFactory;
    private final JwtWriterFactory jwtWriterFactory;

    JwtTokenHelper(JwtJsonBuilderFactory jwtJsonBuilderFactory, JwtWriterFactory jwtWriterFactory) {
        this.jwtJsonBuilderFactory = jwtJsonBuilderFactory;
        this.jwtWriterFactory = jwtWriterFactory;
    }

    /**
     * Generates a JWT token.
     * @param request the canonical http request to generate a token for - Must be of the _stripped relative_ url
     * @param expiry the token expiry (from time of generation) in seconds
     * @param issuerId the issuer ID to embed in the token
     * @param sharedSecret the shared secret to embed in the token
     *
     * @return a JWT token
     */
    public String generateToken(@Nonnull CanonicalHttpUriRequest request, long expiry, @Nonnull String issuerId,
                                String subject, @Nonnull String sharedSecret) throws JwtTokenGenerationException {
        requireNonNull(request, "request");
        requireNonNull(issuerId, "issuerId");
        requireNonNull(sharedSecret, "sharedSecret");

        long issuedAt = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
        long expiresAt = issuedAt + expiry;
        JwtJsonBuilder jwtBuilder = jwtJsonBuilderFactory.jsonBuilder()
                .issuedAt(issuedAt)
                .expirationTime(expiresAt)
                .issuer(issuerId);

        if (subject != null) {
            jwtBuilder.subject(subject);
        }

        try {
            JwtClaimsBuilder.appendHttpRequestClaims(jwtBuilder, request);
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
            throw new JwtTokenGenerationException("Failed to generate JWT token.", e);
        }

        String json = jwtBuilder.build();
        return jwtWriterFactory
                .macSigningWriter(SigningAlgorithm.HS256, sharedSecret)
                .jsonToJwt(json);
    }
}
