package ervu_dashboard.security.service;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import javax.crypto.SecretKey;

import ervu_dashboard.security.model.jwt.UserSession;
import ervu_dashboard.security.model.role.ErvuRoleAuthority;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class ErvuJwtTokenService implements JwtTokenService {

  private final boolean enableJwtValidation;
  private final String issuer;
  private final SecretKey secret;

  public ErvuJwtTokenService(
      @Value("${security.jwt.validation.enable:false}") boolean enableJwtValidation,
      @Value("${security.jwt.validation.issuer:#{null}}") String issuer,
      @Value("${security.jwt.validation.secret-key:#{null}}") String secretKey
  ) {
    this.enableJwtValidation = enableJwtValidation;
    this.issuer = issuer;
    this.secret =
        secretKey != null ? Keys.hmacShaKeyFor(Base64.getDecoder().decode(secretKey)) : null;
  }

  @Override
  public UserSession getUserSession(String token) {
    try {
      if (enableJwtValidation) {
        return parseToken(token);
      }
      else {
        return unsafeParseToken(token);
      }
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  protected UserSession parseToken(String token) {
    JwtParser parser = Jwts.parser();

    if (issuer != null && !issuer.isEmpty()) {
      parser = parser.requireIssuer(issuer);
    }

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

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

    return UserSession.builder()
        .withName(claims.get("name", String.class))
        .withRealm(claims.get("realm", String.class))
        .withRecruitmentId(claims.get("domain_id", String.class))
        .withRoles((getRoles((List<String>) claims.get("roles", ArrayList.class))))
        .build();
  }

  protected UserSession unsafeParseToken(String token) {
    token = token.substring(0, token.lastIndexOf(".") + 1);
    JwtParser parser = Jwts.parser();
    Claims claims = parser
        .setClock(() -> Date.from(Instant.now().minus(999, ChronoUnit.DAYS)))
        .parseClaimsJwt(token).getBody();

    return UserSession.builder()
        .withName(claims.get("name", String.class))
        .withRealm(claims.get("realm", String.class))
        .withRecruitmentId(claims.get("domain_id", String.class))
        .withRoles(getRoles((List<String>) claims.get("roles", ArrayList.class)))
        .build();
  }

  protected Set<ErvuRoleAuthority> getRoles(List<String> roles) {
    return roles
        .stream()
        .map(ErvuRoleAuthority::new)
        .collect(Collectors.toSet());
  }
}
