package com.erp.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 비밀번호 해시 검증/생성 유틸.
 *
 * <p>운영 환경에 MD5 로 저장된 레거시 해시와 BCrypt 신규 해시가 공존하는 점진 마이그레이션 단계용.
 * BCrypt 해시는 항상 {@code $2a$/$2b$/$2y$} prefix 로 시작하므로 prefix 로 알고리즘을 식별한다.
 *
 * <ul>
 *   <li>{@link #matches(String, String)} - 입력 평문과 저장 해시를 비교 (알고리즘 자동 판별)</li>
 *   <li>{@link #encode(String)} - BCrypt 로 신규 해시 생성 (회원가입 / 점진 재해시 용)</li>
 *   <li>{@link #isLegacy(String)} - 저장된 해시가 BCrypt 가 아닌지 (= 재해시 대상인지) 판단</li>
 * </ul>
 */
@Component
public class PasswordHasher {

    private final PasswordEncoder bcryptEncoder;

    @Autowired
    public PasswordHasher(PasswordEncoder bcryptEncoder) {
        this.bcryptEncoder = bcryptEncoder;
    }

    /**
     * 평문 비밀번호가 저장된 해시와 일치하는지.
     * 저장 해시가 BCrypt 면 BCrypt 로, 아니면 MD5 로 비교.
     */
    public boolean matches(String rawPassword, String storedHash) {
        if (rawPassword == null || storedHash == null) {
            return false;
        }
        if (isBcrypt(storedHash)) {
            return bcryptEncoder.matches(rawPassword, storedHash);
        }
        return md5(rawPassword).equals(storedHash);
    }

    /** BCrypt 로 새 해시 생성. 회원가입과 점진 재해시 양쪽에서 사용. */
    public String encode(String rawPassword) {
        return bcryptEncoder.encode(rawPassword);
    }

    /** 저장된 해시가 BCrypt 가 아니면 (즉 레거시 MD5 면) true. 점진 재해시 대상 판별. */
    public boolean isLegacy(String storedHash) {
        return storedHash != null && !isBcrypt(storedHash);
    }

    private boolean isBcrypt(String hash) {
        return hash.startsWith("$2a$") || hash.startsWith("$2b$") || hash.startsWith("$2y$");
    }

    private String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            return String.format("%032x", new BigInteger(1, digest));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 해시 생성 실패", e);
        }
    }
}
