package com.kt.event.common.util;

import com.kt.event.common.exception.InfraException;
import com.kt.event.common.exception.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

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.SecureRandom;
import java.util.Base64;

/**
 * 암호화 유틸리티
 * 비밀번호 해싱 및 데이터 암호화 기능 제공
 */
@Slf4j
public class EncryptionUtil {

    /**
     * BCrypt 인코더 (Cost Factor: 10)
     */
    private static final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(10);

    /**
     * AES-256-GCM 설정
     */
    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int GCM_TAG_LENGTH = 128;
    private static final int GCM_IV_LENGTH = 12;

    /**
     * 비밀번호 해싱 (BCrypt)
     *
     * @param rawPassword 원본 비밀번호
     * @return 해싱된 비밀번호
     */
    public static String hashPassword(String rawPassword) {
        if (StringUtil.isBlank(rawPassword)) {
            throw new InfraException(ErrorCode.COMMON_002, "비밀번호는 필수입니다");
        }
        return passwordEncoder.encode(rawPassword);
    }

    /**
     * 비밀번호 검증 (BCrypt)
     *
     * @param rawPassword    원본 비밀번호
     * @param hashedPassword 해싱된 비밀번호
     * @return 일치 여부
     */
    public static boolean verifyPassword(String rawPassword, String hashedPassword) {
        if (StringUtil.isBlank(rawPassword) || StringUtil.isBlank(hashedPassword)) {
            return false;
        }
        try {
            return passwordEncoder.matches(rawPassword, hashedPassword);
        } catch (Exception e) {
            log.error("Password verification failed", e);
            return false;
        }
    }

    /**
     * AES-256-GCM 암호화
     *
     * @param plainText 평문
     * @param secretKey 비밀키 (32바이트)
     * @return Base64 인코딩된 암호문
     */
    public static String encrypt(String plainText, String secretKey) {
        if (StringUtil.isBlank(plainText)) {
            return plainText;
        }

        try {
            // IV 생성 (12바이트)
            byte[] iv = new byte[GCM_IV_LENGTH];
            SecureRandom random = new SecureRandom();
            random.nextBytes(iv);

            // 암호화
            SecretKey key = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES");
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
            cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);

            byte[] cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

            // IV + 암호문 결합
            ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
            byteBuffer.put(iv);
            byteBuffer.put(cipherText);

            // Base64 인코딩
            return Base64.getEncoder().encodeToString(byteBuffer.array());

        } catch (Exception e) {
            log.error("Encryption failed", e);
            throw new InfraException(ErrorCode.COMMON_004, e);
        }
    }

    /**
     * AES-256-GCM 복호화
     *
     * @param cipherText Base64 인코딩된 암호문
     * @param secretKey  비밀키 (32바이트)
     * @return 평문
     */
    public static String decrypt(String cipherText, String secretKey) {
        if (StringUtil.isBlank(cipherText)) {
            return cipherText;
        }

        try {
            // Base64 디코딩
            byte[] decodedData = Base64.getDecoder().decode(cipherText);

            // IV와 암호문 분리
            ByteBuffer byteBuffer = ByteBuffer.wrap(decodedData);
            byte[] iv = new byte[GCM_IV_LENGTH];
            byteBuffer.get(iv);
            byte[] encrypted = new byte[byteBuffer.remaining()];
            byteBuffer.get(encrypted);

            // 복호화
            SecretKey key = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES");
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
            cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);

            byte[] decryptedData = cipher.doFinal(encrypted);
            return new String(decryptedData, StandardCharsets.UTF_8);

        } catch (Exception e) {
            log.error("Decryption failed", e);
            throw new InfraException(ErrorCode.COMMON_004, e);
        }
    }

    /**
     * 32바이트 비밀키 생성 (개발/테스트용)
     * 실제 운영에서는 환경변수나 Key Management Service 사용 권장
     *
     * @param seed 시드 문자열
     * @return 32바이트 비밀키
     */
    public static String generateSecretKey(String seed) {
        String paddedSeed = (seed + "00000000000000000000000000000000").substring(0, 32);
        return paddedSeed;
    }
}
