package com.eactive.eai.rms.onl.dashboard.modern.backup.mapper;

import com.eactive.eai.rms.common.util.TypeCastUtils;
import com.eactive.eai.rms.onl.dashboard.modern.backup.BackupContentVO;
import com.eactive.eai.rms.onl.dashboard.repository.Dashboard2Repository;
import org.apache.commons.collections.keyvalue.MultiKey;
import org.apache.commons.collections.map.MultiKeyMap;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class Dashboard2RepositoryVoMapper implements BackupMapper<BackupContentVO.DashboardRepository2Fields, Dashboard2Repository> {

    @Override
    public BackupContentVO.DashboardRepository2Fields toVo(Dashboard2Repository dashboard2Repository) throws Exception {

        BackupContentVO.DashboardRepository2Fields dashboardRepository2Fields = new BackupContentVO.DashboardRepository2Fields();
        for (Field field : BackupContentVO.DashboardRepository2Fields.class.getDeclaredFields()) {
            Method method = Arrays.stream(BackupContentVO.DashboardRepository2Fields.class.getDeclaredMethods()).filter(m -> m.getName().equalsIgnoreCase("set" + field.getName())).findAny().orElse(null);
            if (method != null) {
                if ("repoMemTransaction".equalsIgnoreCase(field.getName()) || "repository".equalsIgnoreCase(field.getName())) {
                    method.invoke(dashboardRepository2Fields, multiKeyMapToNormalMap(getReflection(Dashboard2Repository.class, field.getName(), dashboard2Repository)));
                } else {
                    method.invoke(dashboardRepository2Fields, TypeCastUtils.castOrElse(getReflection(Dashboard2Repository.class, field.getName(), dashboard2Repository), field.getType(), null));
                }
            }
        }
        return dashboardRepository2Fields;
    }

    @Override
    public void setVoValuesToObject(BackupContentVO.DashboardRepository2Fields vo, Dashboard2Repository object) throws Exception {
        for (Field field : BackupContentVO.DashboardRepository2Fields.class.getDeclaredFields()) {
            Method method = Arrays.stream(BackupContentVO.DashboardRepository2Fields.class.getDeclaredMethods()).filter(m -> m.getName().equalsIgnoreCase("get" + field.getName())).findAny().orElse(null);
            if (method != null) {
                if ("repoMemTransaction".equalsIgnoreCase(field.getName()) || "repository".equalsIgnoreCase(field.getName())) {
                    setReflection(Dashboard2Repository.class, field.getName(), object, normalMapToMultiKeyMap(TypeCastUtils.castOrElse(method.invoke(vo), Map.class, null)));
                } else {
                    setReflection(Dashboard2Repository.class, field.getName(), object, TypeCastUtils.castOrElse(method.invoke(vo), field.getType(), null));
                }
            }
        }
    }

    private <V> Map<String, V> multiKeyMapToNormalMap(MultiKeyMap multiKeyMap) {
        if (multiKeyMap == null)
            return null;

        Map<String, V> normalMap = new HashMap<>();

        for (Object key : multiKeyMap.keySet()) {
            // MultiKey에서 키 배열 가져오기
            MultiKey multiKey = (MultiKey) key;
            Object[] keys = multiKey.getKeys();

            // 키를 "_"로 join하여 하나의 문자열로 합성
            String combinedKey = Arrays.stream(keys)
                    .map(Object::toString)
                    .collect(Collectors.joining("_"));

            // 합성된 키, 값 -> normalMap
            normalMap.put(combinedKey, (V) multiKeyMap.get(key));
        }

        return normalMap;
    }

    public static <V> MultiKeyMap normalMapToMultiKeyMap(Map<String, V> normalMap) {
        if (normalMap == null)
            return null;

        MultiKeyMap multiKeyMap = new MultiKeyMap();

        for (Map.Entry<String, V> entry : normalMap.entrySet()) {
            String combinedKey = entry.getKey();
            V value = entry.getValue();

            // "_"로 분리하여 subKeys 배열을 얻음
            String[] subKeys = combinedKey.split("_");
            multiKeyMap.put(new MultiKey(subKeys), value);
        }

        return multiKeyMap;
    }

}
