package incheon.uis.ums.util;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Arrays;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

/**
 * 필드 세팅 메서드 분리
 * 2025-11-21 seyoung
 */
@Slf4j
@Component
public class UisDynamicFieldUtils {

    //필드 세팅
    public void setField(Object target, String fieldNm, Object rawValue) {
        Field f = findField(target.getClass(), fieldNm);
        if (f == null) { log.debug("필드 없음: {}", fieldNm); return; }
        boolean acc = f.canAccess(target);
        try {
            f.setAccessible(true);
            Class<?> t = f.getType();
            Object v = coerceValue(t, rawValue);
            f.set(target, v);
        } catch (IllegalAccessException | SecurityException e) {
            log.error("필드 세팅 실패 - {} ({} -> {})",
                    fieldNm,
                    rawValue == null ? "null" : rawValue.getClass().getSimpleName(),
                    f.getType().getSimpleName(),
                    e);
        } finally {
            f.setAccessible(acc);
        }
    }

    //타입 변환
    public Object coerceValue(Class<?> targetType, Object raw) {
        if (raw == null) return null;
        if (targetType.isInstance(raw)) return raw;
        String s = String.valueOf(raw).trim();
        if (targetType == String.class) return s;
        try {
            if (targetType == Long.class || targetType == long.class)       return Long.valueOf(s.replaceAll(",", ""));
            if (targetType == Integer.class || targetType == int.class)     return Integer.valueOf(s.replaceAll(",", ""));
            if (targetType == BigDecimal.class)                             return new BigDecimal(s.replaceAll(",", ""));
            if (targetType == Double.class || targetType == double.class)   return Double.valueOf(s.replaceAll(",", ""));
            if (targetType == Float.class || targetType == float.class)     return Float.valueOf(s.replaceAll(",", ""));
            if (targetType == Boolean.class || targetType == boolean.class) return Boolean.valueOf(s);
        } catch (NumberFormatException ex) {
            log.warn("타입 변환 실패: {} -> {} (값: {})", s, targetType.getSimpleName(), raw, ex);
        }
        return raw;
    }

    //필드 찾기
    public static Field findField(Class<?> type, String name) {
        for (Class<?> c = type; c != null && c != Object.class; c = c.getSuperclass()) {
            try {
                return c.getDeclaredField(name);
            } catch (NoSuchFieldException e) {
                // 정상 흐름: 현재 클래스에 필드가 없으면 상위 클래스로 계속 탐색
                continue;
            }
        }
        return null;
    }

    // typeId xyz:abc_def_gh와 같이 프리픽스와 언더바가 붙어서 오지만, 카멜케이스(xyz:AbcDefGh)로 가공해야 함
    public String strTypeIdToCamel(String strTypeId){
        final String [] rawTypeId = StringUtils.split(strTypeId, ":");
        if(rawTypeId.length > 1)
        {
            return rawTypeId[0] + ":"
             + Arrays.stream(rawTypeId[1].split("_+")) // __가 여럿이 경우도 split 처리
               .filter(s -> !s.isEmpty()) // 빈 문자열 제거
               .map(s -> s.substring(0, 1).toUpperCase() + s.substring(1))
             .reduce("", String::concat);
        }else {
            return Arrays.stream(rawTypeId[0].split("_+")) // __가 여럿이 경우도 split 처리
            .filter(s -> !s.isEmpty()) // 빈 문자열 제거
            .map(s -> s.substring(0, 1).toUpperCase() + s.substring(1))
            .reduce("", String::concat);
        }
    }

    public static String snakeToCamel(String str) {
        if (str == null || str.isEmpty()) return str;

        return Arrays.stream(str.split("_"))
                .map(part -> {
                    if (part.isEmpty()) return part;
                    String lower = part.toLowerCase();
                    return Character.toUpperCase(lower.charAt(0)) + lower.substring(1);
                })
                .reduce((a, b) -> a + b)
                .map(result -> Character.toLowerCase(result.charAt(0)) + result.substring(1))  // 첫 글자만 소문자
                .orElse("");
    }
}
