package com.ga.common.auth;

import com.ga.common.exception.BizException;
import com.ga.common.exception.ErrorCode;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

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

/**
 * JWT 발급/검증.
 */
@Slf4j
@Component
public class JwtTokenProvider {

    private final SecretKey key;
    private final long accessExpire;
    private final long refreshExpire;

    public JwtTokenProvider(
            @Value("${ga.jwt.secret}") String secret,
            @Value("${ga.jwt.access-token-expire:7200000}") long accessExpire,
            @Value("${ga.jwt.refresh-token-expire:604800000}") long refreshExpire
    ) {
        byte[] raw = secret.getBytes(StandardCharsets.UTF_8);
        // 256비트 미만이면 안전하게 패딩
        if (raw.length < 32) {
            byte[] padded = new byte[32];
            System.arraycopy(raw, 0, padded, 0, raw.length);
            raw = padded;
        }
        this.key = Keys.hmacShaKeyFor(raw);
        this.accessExpire = accessExpire;
        this.refreshExpire = refreshExpire;
    }

    public String createAccessToken(Long userId, String loginId, List<String> roles) {
        return create(userId, loginId, roles, accessExpire, "ACCESS");
    }

    public String createRefreshToken(Long userId, String loginId) {
        return create(userId, loginId, List.of(), refreshExpire, "REFRESH");
    }

    private String create(Long userId, String loginId, List<String> roles, long expireMs, String type) {
        Date now = new Date();
        return Jwts.builder()
                .subject(loginId)
                .claims(Map.of(
                        "uid", userId,
                        "roles", roles,
                        "type", type
                ))
                .issuedAt(now)
                .expiration(new Date(now.getTime() + expireMs))
                .signWith(key)
                .compact();
    }

    public Claims parse(String token) {
        try {
            return Jwts.parser().verifyWith(key).build()
                    .parseSignedClaims(token).getPayload();
        } catch (ExpiredJwtException e) {
            throw new BizException(ErrorCode.TOKEN_EXPIRED);
        } catch (JwtException | IllegalArgumentException e) {
            throw new BizException(ErrorCode.TOKEN_INVALID);
        }
    }

    public boolean isValid(String token) {
        try {
            parse(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public long getAccessExpire()  { return accessExpire;  }
    public long getRefreshExpire() { return refreshExpire; }
}
