package com.goldenchart.security;

import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * 업비트 API 키 등 민감 문자열 DB 저장용 AES-256-GCM 암호화.
 * 평문은 API 응답·로그에 노출하지 않는다.
 */
@Service
@Slf4j
public class SecretCryptoService {

    private static final String PREFIX = "ENC:v1:";
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 128;

    @Value("${goldenchart.secrets.encryption-key:}")
    private String encryptionKeyConfig;

    private SecretKey secretKey;
    private final SecureRandom secureRandom = new SecureRandom();

    @PostConstruct
    void init() {
        String raw = encryptionKeyConfig != null ? encryptionKeyConfig.trim() : "";
        if (raw.isEmpty()) {
            raw = "goldenchart-dev-change-in-production";
            log.warn("[SecretCrypto] GC_SECRETS_ENCRYPTION_KEY 미설정 — 개발용 기본 키 사용 (운영 환경에서는 반드시 설정)");
        }
        try {
            byte[] keyBytes = MessageDigest.getInstance("SHA-256")
                    .digest(raw.getBytes(StandardCharsets.UTF_8));
            this.secretKey = new SecretKeySpec(keyBytes, "AES");
        } catch (Exception e) {
            throw new IllegalStateException("암호화 키 초기화 실패", e);
        }
    }

    public boolean isEncrypted(String value) {
        return value != null && value.startsWith(PREFIX);
    }

    /** DB 저장용 암호화 (이미 암호문이면 그대로 반환) */
    public String encrypt(String plaintext) {
        if (plaintext == null || plaintext.isBlank()) {
            return plaintext;
        }
        if (isEncrypted(plaintext)) {
            return plaintext;
        }
        try {
            byte[] iv = new byte[GCM_IV_LENGTH];
            secureRandom.nextBytes(iv);
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(GCM_TAG_LENGTH, iv));
            byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
            ByteBuffer buf = ByteBuffer.allocate(iv.length + encrypted.length);
            buf.put(iv);
            buf.put(encrypted);
            return PREFIX + Base64.getEncoder().encodeToString(buf.array());
        } catch (Exception e) {
            throw new IllegalStateException("민감 정보 암호화 실패", e);
        }
    }

    /** DB에서 읽은 값 복호화 (레거시 평문은 그대로 반환) */
    public String decrypt(String stored) {
        if (stored == null || stored.isBlank()) {
            return stored;
        }
        if (!isEncrypted(stored)) {
            return stored;
        }
        try {
            String payload = stored.substring(PREFIX.length());
            byte[] decoded = Base64.getDecoder().decode(payload);
            ByteBuffer buf = ByteBuffer.wrap(decoded);
            byte[] iv = new byte[GCM_IV_LENGTH];
            buf.get(iv);
            byte[] ciphertext = new byte[buf.remaining()];
            buf.get(ciphertext);
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(GCM_TAG_LENGTH, iv));
            byte[] plain = cipher.doFinal(ciphertext);
            return new String(plain, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("[SecretCrypto] 복호화 실패 — 키 손상 또는 encryption-key 불일치 가능");
            throw new IllegalStateException("민감 정보 복호화 실패", e);
        }
    }

    /** API 응답용 — 마지막 4자만 노출 */
    public static String maskForDisplay(String plain) {
        if (plain == null || plain.length() < 4) {
            return null;
        }
        return "····" + plain.substring(plain.length() - 4);
    }
}
