package marketing.heyday.hartmann.fotodocumentation.core.utils;

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import jakarta.annotation.PostConstruct;
import jakarta.ejb.LocalBean;
import jakarta.ejb.Stateless;

/**
 * 
 * <p>Copyright: Copyright (c) 2024</p>
 * <p>Company: heyday Marketing GmbH</p>
 * @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
 * @version 1.0
 * 
 * created: 21 Jan 2026
 */
@Stateless
@LocalBean
public class JwtTokenUtil {
	private static final Log LOG = LogFactory.getLog(JwtTokenUtil.class);

	private static final long ACCESS_TOKEN_VALIDITY = 60 * 60 * 1000L; // 1 hour                                                                                  
	private static final long REFRESH_TOKEN_VALIDITY = 30 * 24 * 60 * 60 * 1000L; // 30 days                                                                     
	private static final long TEMP_2FA_TOKEN_VALIDITY = 5 * 60 * 1000L; // 5 minutes                                                                              

	private static final String ISSUER = "foto-jwt-issuer";
	private static final String AUDIENCE = "foto-api";

	private PrivateKey privateKey;
	private PublicKey publicKey;

	@PostConstruct
	public void init() {
		// Load key from wildfly system property
		try {
			String pem = System.getProperty("jwt.secret.key");
			pem = pem.replace("-----BEGIN PRIVATE KEY-----", "")
					.replace("-----END PRIVATE KEY-----", "")
					.replaceAll("\\s", "");
			byte[] decoded = Base64.getDecoder().decode(pem);

			PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
			privateKey = KeyFactory.getInstance("RSA").generatePrivate(spec);
		} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
			LOG.error("Failed to load JWT PrivateKey " + e.getMessage(), e);
		}
	}

	public String generateAccessToken(Long userId, String username, List<String> groups) {
		return Jwts.builder()
				.issuer(ISSUER)
				.audience().add(AUDIENCE).and()
				.subject(userId.toString())
				.claim("username", username)
				.claim("type", "access")
				.claim("groups", groups)
				.issuedAt(new Date())
				.expiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_VALIDITY))
				.signWith(privateKey, Jwts.SIG.RS256)
				.compact();
	}

	public String generateRefreshToken(Long userId) {
		return Jwts.builder()
				.issuer(ISSUER)
				.audience().add(AUDIENCE).and()
				.subject(userId.toString())
				.claim("type", "refresh")
				.issuedAt(new Date())
				.expiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_VALIDITY))
				.signWith(privateKey, Jwts.SIG.RS256)
				.compact();
	}

	public String generateTemp2FAToken(Long userId) {
		return Jwts.builder()
				.issuer(ISSUER)
				.audience().add(AUDIENCE).and()
				.subject(userId.toString())
				.claim("type", "temp_2fa")
				.issuedAt(new Date())
				.expiration(new Date(System.currentTimeMillis() + TEMP_2FA_TOKEN_VALIDITY))
				.signWith(privateKey, Jwts.SIG.RS256)
				.compact();
	}

	public Claims validateAndExtractClaims(String token) {
		return Jwts.parser()
				.verifyWith(publicKey) // FIXME: not working need public key that we currently didn't load
				.build()
				.parseUnsecuredClaims(token)
				.getPayload();
	}

	public Long extractUserId(String token) {
		Claims claims = validateAndExtractClaims(token);
		return Long.parseLong(claims.getSubject());
	}

	public boolean isTokenExpired(String token) {
		try {
			Claims claims = validateAndExtractClaims(token);
			return claims.getExpiration().before(new Date());
		} catch (JwtException e) {
			LOG.warn("Failed to get expiration date from token " + e.getMessage());
			return true;
		}
	}
}
