package com.gameplatform.server.security;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Map;

@Component
public class JwtService {
    private static final Logger log = LoggerFactory.getLogger(JwtService.class);
    private final SecretKey key;
    private final long accessTokenMinutes;

    public JwtService(@Value("${security.jwt.secret}") String secret,
                      @Value("${security.jwt.access-token-minutes:30}") long accessTokenMinutes) {
        // accept raw text secret; if base64 provided, still works with Decoders
        byte[] bytes = secret.length() < 32 ? (secret + "_pad_to_32_chars_secret_key_value").getBytes() : secret.getBytes();
        this.key = Keys.hmacShaKeyFor(bytes);
        this.accessTokenMinutes = accessTokenMinutes;
        // Initialization log without exposing secret content
        log.info("JWT服务初始化完成 - 密钥长度: {} bytes, token过期时间: {} 分钟", bytes.length, accessTokenMinutes);
    }

    public String generateToken(String subject, String userType, Long userId, String username, Map<String, Object> extra) {
        // Avoid logging sensitive parameters
        log.debug("开始生成JWT token");
        
        Instant now = Instant.now();
        var builder = Jwts.builder()
                .setSubject(subject)
                .setIssuedAt(Date.from(now))
                .setExpiration(Date.from(now.plus(accessTokenMinutes, ChronoUnit.MINUTES)))
                .claim("userType", userType)
                .claim("userId", userId)
                .claim("username", username);
        if (extra != null) {
            extra.forEach(builder::claim);
        }
        
        String token = builder.signWith(key, SignatureAlgorithm.HS256).compact();
        log.debug("JWT token生成成功，长度: {} 字符，过期: {} 分钟", token.length(), accessTokenMinutes);
        
        return token;
    }

    /**
     * 生成链接code（用于用户端访问链接）
     */
    public String generateLinkCode(Long linkId, String codeNo, int expireHours) {
        // Do not log identifiers or codes
        log.debug("开始生成链接code，过期小时: {}", expireHours);
        
        Instant now = Instant.now();
        var builder = Jwts.builder()
                .setSubject("link_access")
                .setIssuedAt(Date.from(now))
                .setExpiration(Date.from(now.plus(expireHours, ChronoUnit.HOURS)))
                .claim("linkId", linkId)
                .claim("codeNo", codeNo)
                .claim("type", "link_access");
        
        String code = builder.signWith(key, SignatureAlgorithm.HS256).compact();
        log.debug("链接code生成成功，长度: {} 字符，过期: {} 小时", code.length(), expireHours);
        return code;
    }

    /**
     * 解析链接code获取链接信息
     */
    public LinkCodeInfo parseLinkCode(String code) {
        log.debug("开始解析链接code");
        
        try {
            io.jsonwebtoken.Claims claims = Jwts.parserBuilder()
                    .setSigningKey(key)
                    .build()
                    .parseClaimsJws(code)
                    .getBody();
            
            // 检查是否是链接类型的token
            String type = claims.get("type", String.class);
            if (!"link_access".equals(type)) {
                throw new IllegalArgumentException("无效的链接code类型");
            }
            
            Long linkId = claims.get("linkId", Long.class);
            String codeNo = claims.get("codeNo", String.class);
            
            if (linkId == null || codeNo == null) {
                throw new IllegalArgumentException("链接code格式错误");
            }
            
            LinkCodeInfo info = new LinkCodeInfo();
            info.setLinkId(linkId);
            info.setCodeNo(codeNo);
            info.setIssuedAt(claims.getIssuedAt());
            info.setExpireAt(claims.getExpiration());
            
            log.debug("链接code解析成功");
            
            return info;
        } catch (Exception e) {
            log.warn("链接code解析失败: {}", e.getMessage());
            throw new IllegalArgumentException("无效的链接code", e);
        }
    }

    /**
     * 链接code信息
     */
    public static class LinkCodeInfo {
        private Long linkId;
        private String codeNo;
        private Date issuedAt;
        private Date expireAt;
        
        public Long getLinkId() { return linkId; }
        public void setLinkId(Long linkId) { this.linkId = linkId; }
        
        public String getCodeNo() { return codeNo; }
        public void setCodeNo(String codeNo) { this.codeNo = codeNo; }
        
        public Date getIssuedAt() { return issuedAt; }
        public void setIssuedAt(Date issuedAt) { this.issuedAt = issuedAt; }
        
        public Date getExpireAt() { return expireAt; }
        public void setExpireAt(Date expireAt) { this.expireAt = expireAt; }
        
        public boolean isExpired() {
            return expireAt != null && new Date().after(expireAt);
        }
    }

    public io.jsonwebtoken.Claims parse(String token) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(key)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        } catch (io.jsonwebtoken.ExpiredJwtException e) {
            log.debug("JWT token已过期");
            throw e;
        } catch (io.jsonwebtoken.JwtException e) {
            log.debug("JWT token无效: {}", e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("JWT解析异常: {}", e.getMessage());
            throw e;
        }
    }
}
