package ru.micord.ervu.account_applications.security.service;

import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import ru.micord.ervu.account_applications.security.exception.JwtProcessingException;
import ru.micord.ervu.account_applications.security.model.jwt.UserClaims;

@Service
public class ErvuJwtTokenService implements JwtTokenService {
  private final boolean enableJwtValidation;
  private final String issuer;
  private final PublicKey publicKey;

  public ErvuJwtTokenService(
      @Value("${security.jwt.validation.enable:false}") boolean enableJwtValidation,
      @Value("${security.jwt.validation.issuer:#{null}}") String issuer,
      @Value("${security.jwt.validation.public_key:#{null}}") String publicKeyStr
  ) {
    this.enableJwtValidation = enableJwtValidation;
    this.issuer = issuer;
    this.publicKey =
        publicKeyStr != null ? extractPublicKey(publicKeyStr) : null;
  }

  @Override
  public UserClaims getUserClaims(String token) {
    return enableJwtValidation ? parseToken(token) : unsafeParseToken(token);
  }

  protected UserClaims unsafeParseToken(String token) {
    try {
      token = token.substring(0, token.lastIndexOf(".") + 1);
      JwtParser parser = Jwts.parser();
      Claims claims = parser.parseClaimsJwt(token).getBody();

      return UserClaims.builder()
          .withUserId(claims.getSubject())
          .withName(claims.get("name", String.class))
          .withRealm(claims.get("realm", String.class))
          .withDomainId(claims.get("domain_id", String.class))
          .build();
    }
    catch (JwtException e) {
      throw new JwtProcessingException(e.getMessage());
    }
  }

 protected UserClaims parseToken(String token) {
    JwtParser parser = Jwts.parser();
    try {
      if (issuer != null && !issuer.isEmpty()) {
        parser = parser.requireIssuer(issuer);
      }

      if (publicKey != null) {
        parser = parser.setSigningKey(publicKey);
      }

      Claims claims = parser.parseClaimsJws(token).getBody();

      return UserClaims.builder()
          .withUserId(claims.getSubject())
          .withName(claims.get("name", String.class))
          .withRealm(claims.get("realm", String.class))
          .withDomainId(claims.get("domain_id", String.class))
          .build();
    }
    catch (JwtException e) {
      throw new JwtProcessingException(e.getMessage());
    }
  }

  private PublicKey extractPublicKey(String publicKeyStr) {
    try {
      byte[] decodedPublicKey = Base64.getDecoder().decode(publicKeyStr);
      X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decodedPublicKey);
      KeyFactory keyFactory = KeyFactory.getInstance("RSA");
      return keyFactory.generatePublic(x509EncodedKeySpec);
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}
