package com.lesingle.creation.common.util;

import com.lesingle.creation.common.config.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * JWT Token 工具类
 *
 * @author lesingle
 * @since 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenUtil {

    private final JwtProperties jwtProperties;

    /**
     * 获取密钥
     */
    private SecretKey getSigningKey() {
        byte[] keyBytes = jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8);
        return Keys.hmacShaKeyFor(keyBytes);
    }

    /**
     * 从 Token 中获取用户名
     */
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    /**
     * 从 Token 中获取用户 ID
     *
     * @param token JWT Token
     * @return 用户 ID
     */
    public Long getUserIdFromToken(String token) {
        String username = getUsernameFromToken(token);
        try {
            return Long.parseLong(username);
        } catch (NumberFormatException e) {
            log.warn("无法解析用户 ID：{}", username);
            return null;
        }
    }

    /**
     * 从 Token 中获取指定声明
     */
    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 获取所有声明
     */
    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser()
            .verifyWith(getSigningKey())
            .build()
            .parseSignedClaims(token)
            .getPayload();
    }

    /**
     * 生成 Token
     */
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, username);
    }

    /**
     * 生成带额外信息的 Token
     */
    public String generateToken(String username, Map<String, Object> claims) {
        return createToken(claims, username);
    }

    /**
     * 生成带租户信息的 Token
     *
     * @param userId 用户 ID
     * @param tenantId 租户 ID
     * @param tenantCode 租户编码
     * @return JWT Token
     */
    public String generateToken(Long userId, Long tenantId, String tenantCode) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("tenantId", tenantId);
        claims.put("tenantCode", tenantCode);
        return createToken(claims, String.valueOf(userId));
    }

    /**
     * 生成带用户名的 Token（用于公众用户）
     *
     * @param userId 用户 ID
     * @param username 用户名
     * @return JWT Token
     */
    public String generateToken(Long userId, String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        return createToken(claims, username);
    }

    /**
     * 从 Token 中获取租户 ID
     *
     * @param token JWT Token
     * @return 租户 ID
     */
    public Long getTenantIdFromToken(String token) {
        return getClaimFromToken(token, claims -> claims.get("tenantId", Long.class));
    }

    /**
     * 从 Token 中获取租户编码
     *
     * @param token JWT Token
     * @return 租户编码
     */
    public String getTenantCodeFromToken(String token) {
        return getClaimFromToken(token, claims -> claims.get("tenantCode", String.class));
    }

    /**
     * 创建 Token
     */
    private String createToken(Map<String, Object> claims, String subject) {
        Date now = new Date();
        Date expirationDate = new Date(now.getTime() + jwtProperties.getExpiration());

        return Jwts.builder()
            .claims(claims)
            .subject(subject)
            .issuedAt(now)
            .expiration(expirationDate)
            .signWith(getSigningKey())
            .compact();
    }

    /**
     * 验证 Token 是否有效
     */
    public boolean isTokenValid(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    /**
     * 判断 Token 是否过期
     */
    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    /**
     * 获取 Token 过期时间
     */
    public Date extractExpiration(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }
}
