package com.social.media.infrastructure.security;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Service for JWT token operations
 */
@Service
public class JwtService {

    @Value("${jwt.secret:mySecretKey12345678901234567890123456789012345678901234567890}")
    private String jwtSecret;

    @Value("${jwt.access.expiration:86400}") // 24 hours in seconds
    private long accessTokenExpiration;

    @Value("${jwt.refresh.expiration:604800}") // 7 days in seconds  
    private long refreshTokenExpiration;

    /**
     * Generate access token
     */
    public String generateAccessToken(String email, Long userId, String userType) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("userType", userType);
        claims.put("tokenType", "access");
        
        return createToken(claims, email, accessTokenExpiration);
    }

    /**
     * Generate refresh token
     */
    public String generateRefreshToken(String email) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("tokenType", "refresh");
        
        return createToken(claims, email, refreshTokenExpiration);
    }

    /**
     * Extract email from access token
     */
    public String extractEmailFromAccessToken(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * Extract email from refresh token
     */
    public String extractEmailFromRefreshToken(String token) {
        Claims claims = extractAllClaims(token);
        String tokenType = (String) claims.get("tokenType");
        
        if (!"refresh".equals(tokenType)) {
            throw new JwtException("Invalid refresh token");
        }
        
        return claims.getSubject();
    }

    /**
     * Extract user ID from access token
     */
    public Long extractUserIdFromAccessToken(String token) {
        Claims claims = extractAllClaims(token);
        return claims.get("userId", Long.class);
    }

    /**
     * Extract user type from access token
     */
    public String extractUserTypeFromAccessToken(String token) {
        Claims claims = extractAllClaims(token);
        return (String) claims.get("userType");
    }

    /**
     * Validate token
     */
    public boolean isTokenValid(String token, String email) {
        try {
            final String tokenEmail = extractEmailFromAccessToken(token);
            return tokenEmail.equals(email) && !isTokenExpired(token);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Check if token is expired
     */
    public boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    /**
     * Get access token expiration time in seconds
     */
    public long getAccessTokenExpirationTime() {
        return accessTokenExpiration;
    }

    /**
     * Get refresh token expiration time in seconds
     */
    public long getRefreshTokenExpirationTime() {
        return refreshTokenExpiration;
    }

    /**
     * Extract expiration date from token
     */
    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    /**
     * Extract specific claim from token
     */
    private <T> T extractClaim(String token, java.util.function.Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    /**
     * Extract all claims from token
     */
    private Claims extractAllClaims(String token) {
        return Jwts.parser()
                .verifyWith(getSigningKey())
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }

    /**
     * Create JWT token
     */
    private String createToken(Map<String, Object> claims, String subject, long expiration) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration * 1000);

        return Jwts.builder()
                .claims(claims)
                .subject(subject)
                .issuedAt(now)
                .expiration(expiryDate)
                .signWith(getSigningKey())
                .compact();
    }

    /**
     * Get signing key for JWT
     */
    private SecretKey getSigningKey() {
        return Keys.hmacShaKeyFor(jwtSecret.getBytes());
    }
}
