package com.huimv.employment.service.auth;

import com.huimv.employment.common.exception.BizException;
import com.huimv.employment.common.exception.ErrorCode;
import com.huimv.employment.service.config.SecurityProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.stereotype.Service;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.UUID;

/**
 * JWT Token 签发服务。
 * <p>
 * Token 载荷约定（与技术方案 §8.1 对齐）：
 * <ul>
 *   <li>{@code sub} — 用户 ID（fe_user.id）</li>
 *   <li>{@code userType} — 用户类型（enterprise / worker）</li>
 *   <li>{@code jti} — Token 唯一标识，后续可用于黑名单/登出</li>
 *   <li>{@code exp} — 过期时间</li>
 * </ul>
 * 客户端请求时在 Header 携带：{@code Authorization: Bearer <token>}
 * </p>
 */
@Service
public class JwtTokenService {

    private final SecurityProperties securityProperties;

    public JwtTokenService(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }

    /**
     * 为用户签发访问 Token。
     *
     * @param userId   fe_user 主键
     * @param userType 用户类型：enterprise（企业主）或 worker（临时工）
     * @return Token 字符串、过期秒数及 jti
     */
    public TokenIssueResult issueToken(Long userId, String userType) {
        long expireSeconds = securityProperties.getJwtExpireSeconds();
        Date now = new Date();
        Date expireAt = new Date(now.getTime() + expireSeconds * 1000);
        // jti 用于 Token 唯一标识，后续可配合 Redis 实现登出黑名单
        String jti = UUID.randomUUID().toString().replace("-", "");

        String token = Jwts.builder()
                .setSubject(String.valueOf(userId))
                .claim("userType", userType)
                .setId(jti)
                .setIssuedAt(now)
                .setExpiration(expireAt)
                .signWith(signingKey(), SignatureAlgorithm.HS256)
                .compact();

        return new TokenIssueResult(token, expireSeconds, jti);
    }

    /**
     * 解析并校验 JWT，提取用户 ID 与 userType。
     *
     * @param token Bearer 后的 JWT 字符串
     * @return 解析结果
     */
    public TokenClaims parseToken(String token) {
        try {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(signingKey())
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
            Long userId = Long.valueOf(claims.getSubject());
            String userType = claims.get("userType", String.class);
            return new TokenClaims(userId, userType, claims.getId());
        } catch (ExpiredJwtException | MalformedJwtException | SignatureException | IllegalArgumentException ex) {
            throw new BizException(ErrorCode.UNAUTHORIZED);
        }
    }

    /**
     * 构建 HS256 签名密钥。
     * <p>JJWT 要求 HMAC-SHA256 密钥至少 256 bit（32 字节），不足时右侧补零填充。</p>
     */
    private SecretKey signingKey() {
        byte[] keyBytes = securityProperties.getJwtSecret().getBytes(StandardCharsets.UTF_8);
        if (keyBytes.length < 32) {
            byte[] padded = new byte[32];
            System.arraycopy(keyBytes, 0, padded, 0, keyBytes.length);
            keyBytes = padded;
        }
        return Keys.hmacShaKeyFor(keyBytes);
    }

    /** Token 签发结果 */
    public static class TokenIssueResult {
        /** JWT 字符串 */
        private final String token;
        /** 有效时长（秒） */
        private final long expiresIn;
        /** Token 唯一 ID，可用于 Redis 黑名单 key：fe:token:{jti} */
        private final String jti;

        public TokenIssueResult(String token, long expiresIn, String jti) {
            this.token = token;
            this.expiresIn = expiresIn;
            this.jti = jti;
        }

        public String getToken() {
            return token;
        }

        public long getExpiresIn() {
            return expiresIn;
        }

        public String getJti() {
            return jti;
        }
    }

    /** JWT 解析结果 */
    public static class TokenClaims {
        private final Long userId;
        private final String userType;
        private final String jti;

        public TokenClaims(Long userId, String userType, String jti) {
            this.userId = userId;
            this.userType = userType;
            this.jti = jti;
        }

        public Long getUserId() {
            return userId;
        }

        public String getUserType() {
            return userType;
        }

        public String getJti() {
            return jti;
        }
    }
}
