package com.gnx.telemedicine.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.io.DecodingException;
import io.jsonwebtoken.io.Decoders;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Date;

@Component
@Slf4j
public class JwtUtils {
    @Value("${jwt.secret}")
    private String jwtSecret;

    @Value("${jwt.expiration}")
    private int jwtExpiration;
    
    @Value("${jwt.refresh-expiration:604800}") // Default: 7 days
    private int refreshExpiration;

    private javax.crypto.SecretKey getSigningKey() {
        byte[] keyBytes = null;
        try {
            keyBytes = Decoders.BASE64.decode(jwtSecret);
        } catch (DecodingException | IllegalArgumentException ignored) {
            // Not valid Base64, we'll derive from UTF-8 string below
        }

        if (keyBytes != null && keyBytes.length >= 32) {
            return Keys.hmacShaKeyFor(keyBytes);
        }

        // Fallback: derive a stable 256-bit key from the provided secret using SHA-256
        try {
            java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
            byte[] derived = digest.digest(jwtSecret.getBytes(StandardCharsets.UTF_8));
            return Keys.hmacShaKeyFor(derived);
        } catch (java.security.NoSuchAlgorithmException e) {
            throw new IllegalStateException("SHA-256 algorithm not available for JWT key derivation", e);
        }
    }

    public String generateToken(String email) {
        return Jwts.builder()
                .subject(email)
                .issuedAt(new Date())
                .expiration(new Date(new Date().getTime() + jwtExpiration * 1000L))
                .signWith(getSigningKey())
                .compact();
    }
    
    /**
     * Generate a refresh token (long-lived token for obtaining new access tokens).
     */
    public String generateRefreshToken(String email) {
        return Jwts.builder()
                .subject(email)
                .claim("type", "refresh")
                .issuedAt(new Date())
                .expiration(new Date(new Date().getTime() + refreshExpiration * 1000L))
                .signWith(getSigningKey())
                .compact();
    }
    
    /**
     * Validate refresh token.
     */
    public boolean validateRefreshToken(String token) {
        try {
            Claims claims = Jwts.parser()
                    .verifyWith(getSigningKey())
                    .build()
                    .parseSignedClaims(token)
                    .getPayload();
            
            // Check if it's a refresh token
            String type = claims.get("type", String.class);
            if (!"refresh".equals(type)) {
                log.warn("Token is not a refresh token");
                return false;
            }
            
            Date expiration = claims.getExpiration();
            boolean isExpired = expiration.before(new Date());
            
            if (isExpired) {
                log.warn("Refresh token expired. Expiration: {}, Current: {}", expiration, new Date());
            }
            
            return !isExpired;
            
        } catch (JwtException e) {
            log.warn("Refresh token validation exception: {} - {}", e.getClass().getSimpleName(), e.getMessage());
            return false;
        } catch (Exception e) {
            log.error("Refresh token validation error: {} - {}", e.getClass().getSimpleName(), e.getMessage(), e);
            return false;
        }
    }

    public boolean validateToken(String token) {
        try {
            Claims claims = Jwts.parser()
                    .verifyWith(getSigningKey())
                    .build()
                    .parseSignedClaims(token)
                    .getPayload();

            Date expiration = claims.getExpiration();
            boolean isExpired = expiration.before(new Date());
            
            if (isExpired) {
                log.warn("JWT token expired. Expiration: {}, Current: {}", expiration, new Date());
            }
            
            return !isExpired;

        } catch (JwtException e) {
            log.warn("JWT validation exception: {} - {}", e.getClass().getSimpleName(), e.getMessage());
            return false;
        } catch (Exception e) {
            log.error("JWT validation error: {} - {}", e.getClass().getSimpleName(), e.getMessage(), e);
            return false;
        }
    }

    public String getEmailFromToken(String token) {
        return Jwts.parser()
                .verifyWith(getSigningKey())
                .build()
                .parseSignedClaims(token)
                .getPayload()
                .getSubject();
    }
}
