package egovframework.util;

import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;

/**
 * 숫자 관련 공통 유틸리티 클래스
 */
@Component
public class NumberUtil {

    /**
     * 문자열을 정수(int)로 변환
     * @param str 변환할 문자열
     * @param defaultValue 변환 실패 시 반환할 기본값
     * @return 변환된 정수 또는 기본값
     */
    public static int toInt(String str, int defaultValue) {
        if (StringUtil.isEmpty(str)) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(str.trim());
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
    
    /**
     * 문자열을 정수(long)로 변환
     * @param str 변환할 문자열
     * @param defaultValue 변환 실패 시 반환할 기본값
     * @return 변환된 정수 또는 기본값
     */
    public static long toLong(String str, long defaultValue) {
        if (StringUtil.isEmpty(str)) {
            return defaultValue;
        }
        try {
            return Long.parseLong(str.trim());
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
    
    /**
     * 문자열을 실수(double)로 변환
     * @param str 변환할 문자열
     * @param defaultValue 변환 실패 시 반환할 기본값
     * @return 변환된 실수 또는 기본값
     */
    public static double toDouble(String str, double defaultValue) {
        if (StringUtil.isEmpty(str)) {
            return defaultValue;
        }
        try {
            return Double.parseDouble(str.trim());
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
    
    /**
     * 문자열을 BigDecimal로 변환
     * @param str 변환할 문자열
     * @param defaultValue 변환 실패 시 반환할 기본값
     * @return 변환된 BigDecimal 또는 기본값
     */
    public static BigDecimal toBigDecimal(String str, BigDecimal defaultValue) {
        if (StringUtil.isEmpty(str)) {
            return defaultValue;
        }
        try {
            return new BigDecimal(str.trim());
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
    
    /**
     * 숫자를 천 단위 구분 기호가 포함된 문자열로 변환
     * @param number 변환할 숫자
     * @return 천 단위 구분 기호가 포함된 문자열
     */
    public static String formatWithComma(long number) {
        return NumberFormat.getNumberInstance(Locale.KOREA).format(number);
    }
    
    /**
     * 숫자를 천 단위 구분 기호가 포함된 문자열로 변환
     * @param number 변환할 숫자
     * @return 천 단위 구분 기호가 포함된 문자열
     */
    public static String formatWithComma(double number) {
        return NumberFormat.getNumberInstance(Locale.KOREA).format(number);
    }
    
    /**
     * 숫자를 천 단위 구분 기호가 포함된 문자열로 변환
     * @param number 변환할 숫자
     * @return 천 단위 구분 기호가 포함된 문자열
     */
    public static String formatWithComma(BigDecimal number) {
        if (number == null) {
            return "";
        }
        return NumberFormat.getNumberInstance(Locale.KOREA).format(number);
    }
    
    /**
     * 숫자를 원화 형식의 문자열로 변환
     * @param number 변환할 숫자
     * @return 원화 형식의 문자열 (예: "₩1,000")
     */
    public static String formatKRW(long number) {
        return NumberFormat.getCurrencyInstance(Locale.KOREA).format(number);
    }
    
    /**
     * 숫자를 원화 형식의 문자열로 변환
     * @param number 변환할 숫자
     * @return 원화 형식의 문자열 (예: "₩1,000.50")
     */
    public static String formatKRW(double number) {
        return NumberFormat.getCurrencyInstance(Locale.KOREA).format(number);
    }
    
    /**
     * 숫자를 원화 형식의 문자열로 변환
     * @param number 변환할 숫자
     * @return 원화 형식의 문자열 (예: "₩1,000.50")
     */
    public static String formatKRW(BigDecimal number) {
        if (number == null) {
            return "";
        }
        return NumberFormat.getCurrencyInstance(Locale.KOREA).format(number);
    }
    
    /**
     * 숫자를 달러 형식의 문자열로 변환
     * @param number 변환할 숫자
     * @return 달러 형식의 문자열 (예: "$1,000")
     */
    public static String formatUSD(long number) {
        return NumberFormat.getCurrencyInstance(Locale.US).format(number);
    }
    
    /**
     * 숫자를 달러 형식의 문자열로 변환
     * @param number 변환할 숫자
     * @return 달러 형식의 문자열 (예: "$1,000.50")
     */
    public static String formatUSD(double number) {
        return NumberFormat.getCurrencyInstance(Locale.US).format(number);
    }
    
    /**
     * 숫자를 달러 형식의 문자열로 변환
     * @param number 변환할 숫자
     * @return 달러 형식의 문자열 (예: "$1,000.50")
     */
    public static String formatUSD(BigDecimal number) {
        if (number == null) {
            return "";
        }
        return NumberFormat.getCurrencyInstance(Locale.US).format(number);
    }
    
    /**
     * 숫자를 지정된 소수점 자리수로 반올림
     * @param number 반올림할 숫자
     * @param scale 소수점 자리수
     * @return 반올림된 숫자
     */
    public static double round(double number, int scale) {
        return BigDecimal.valueOf(number)
                .setScale(scale, RoundingMode.HALF_UP)
                .doubleValue();
    }
    
    /**
     * BigDecimal을 지정된 소수점 자리수로 반올림
     * @param number 반올림할 숫자
     * @param scale 소수점 자리수
     * @return 반올림된 숫자
     */
    public static BigDecimal round(BigDecimal number, int scale) {
        if (number == null) {
            return null;
        }
        return number.setScale(scale, RoundingMode.HALF_UP);
    }
    
    /**
     * 숫자를 지정된 소수점 자리수로 내림
     * @param number 내림할 숫자
     * @param scale 소수점 자리수
     * @return 내림된 숫자
     */
    public static double floor(double number, int scale) {
        return BigDecimal.valueOf(number)
                .setScale(scale, RoundingMode.FLOOR)
                .doubleValue();
    }
    
    /**
     * BigDecimal을 지정된 소수점 자리수로 내림
     * @param number 내림할 숫자
     * @param scale 소수점 자리수
     * @return 내림된 숫자
     */
    public static BigDecimal floor(BigDecimal number, int scale) {
        if (number == null) {
            return null;
        }
        return number.setScale(scale, RoundingMode.FLOOR);
    }
    
    /**
     * 숫자를 지정된 소수점 자리수로 올림
     * @param number 올림할 숫자
     * @param scale 소수점 자리수
     * @return 올림된 숫자
     */
    public static double ceil(double number, int scale) {
        return BigDecimal.valueOf(number)
                .setScale(scale, RoundingMode.CEILING)
                .doubleValue();
    }
    
    /**
     * BigDecimal을 지정된 소수점 자리수로 올림
     * @param number 올림할 숫자
     * @param scale 소수점 자리수
     * @return 올림된 숫자
     */
    public static BigDecimal ceil(BigDecimal number, int scale) {
        if (number == null) {
            return null;
        }
        return number.setScale(scale, RoundingMode.CEILING);
    }
    
    /**
     * 숫자를 지정된 패턴으로 포맷팅
     * @param number 포맷팅할 숫자
     * @param pattern 패턴 (예: "#,###.##", "0.00")
     * @return 포맷팅된 문자열
     */
    public static String format(double number, String pattern) {
        DecimalFormat df = new DecimalFormat(pattern);
        return df.format(number);
    }
    
    /**
     * 숫자를 지정된 패턴으로 포맷팅
     * @param number 포맷팅할 숫자
     * @param pattern 패턴 (예: "#,###.##", "0.00")
     * @return 포맷팅된 문자열
     */
    public static String format(BigDecimal number, String pattern) {
        if (number == null) {
            return "";
        }
        DecimalFormat df = new DecimalFormat(pattern);
        return df.format(number);
    }
    
    /**
     * 두 숫자 중 최소값 반환
     * @param a 첫 번째 숫자
     * @param b 두 번째 숫자
     * @return 최소값
     */
    public static int min(int a, int b) {
        return Math.min(a, b);
    }
    
    /**
     * 두 숫자 중 최대값 반환
     * @param a 첫 번째 숫자
     * @param b 두 번째 숫자
     * @return 최대값
     */
    public static int max(int a, int b) {
        return Math.max(a, b);
    }
    
    /**
     * 숫자가 지정된 범위 내에 있는지 확인
     * @param number 확인할 숫자
     * @param min 최소값
     * @param max 최대값
     * @return 범위 내에 있으면 true, 그렇지 않으면 false
     */
    public static boolean isBetween(int number, int min, int max) {
        return number >= min && number <= max;
    }
    
    /**
     * 숫자가 지정된 범위 내에 있는지 확인
     * @param number 확인할 숫자
     * @param min 최소값
     * @param max 최대값
     * @return 범위 내에 있으면 true, 그렇지 않으면 false
     */
    public static boolean isBetween(double number, double min, double max) {
        return number >= min && number <= max;
    }
    
    /**
     * 숫자가 양수인지 확인
     * @param number 확인할 숫자
     * @return 양수이면 true, 그렇지 않으면 false
     */
    public static boolean isPositive(int number) {
        return number > 0;
    }
    
    /**
     * 숫자가 음수인지 확인
     * @param number 확인할 숫자
     * @return 음수이면 true, 그렇지 않으면 false
     */
    public static boolean isNegative(int number) {
        return number < 0;
    }
    
    /**
     * 숫자가 0인지 확인
     * @param number 확인할 숫자
     * @return 0이면 true, 그렇지 않으면 false
     */
    public static boolean isZero(int number) {
        return number == 0;
    }
    
    /**
     * 숫자가 0인지 확인 (부동소수점 오차 고려)
     * @param number 확인할 숫자
     * @return 0이면 true, 그렇지 않으면 false
     */
    public static boolean isZero(double number) {
        return Math.abs(number) < 0.000001;
    }
}