package enea.lecservice.security.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

@Service
public class JWTUtil {
    private String SECRET_KEY = "#\\Ph&$V&r;%$!knr.lB9V].3jR>U}.yxGFa\".!HY?GmVZ$snWIcaH)>v73>T\\]p\n";
    private long EXPIRATION = 1000 * 60 * 60 * 10;
    private long INVALIDATE = 1000;

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {
        return createToken(claims, subject, null, null);
    }

    public String generateToken(UserDetails userDetails, Date issuedAt, Date expireAt) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername(), issuedAt, expireAt);
    }

    private String createToken(Map<String, Object> claims, String subject, Date issuedAt, Date expireAt) {

        return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(issuedAt != null ? issuedAt : new Date())
                .setExpiration(expireAt != null ? expireAt : calculateExpirationDate(new Date(), EXPIRATION))
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY).compact();
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    private Date calculateExpirationDate(Date createdDate, long expireTime) {
        return new Date(createdDate.getTime() + expireTime);
    }

    public String refreshToken(String token) {
        final Date createdDate = new Date();
        final Date expirationDate = calculateExpirationDate(createdDate, EXPIRATION);

        final Claims claims = extractAllClaims(token);
        claims.setIssuedAt(createdDate);
        claims.setExpiration(expirationDate);

        return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, SECRET_KEY).compact();
    }

    public void expireToken(String token) {
        final Date createdDate = new Date();
        final Date expirationDate = new Date(createdDate.getTime() + INVALIDATE);

        final Claims claims = extractAllClaims(token);
        claims.setIssuedAt(createdDate);
        claims.setExpiration(expirationDate);

        Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, SECRET_KEY).compact();
    }

    public Optional<String> getTokenFromRequest(HttpServletRequest request) {
        final String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Auth-Token ")) {
            return Optional.of(authHeader.substring(11));
        }

        return Optional.empty();
    }
}
