package tech.easyflow.ai.easyagents.tool;

import com.easyagents.flow.core.chain.ChainDefinition;
import com.easyagents.flow.core.chain.repository.ChainDefinitionRepository;
import com.easyagents.flow.core.chain.repository.InMemoryChainStateRepository;
import com.easyagents.flow.core.chain.repository.InMemoryNodeStateRepository;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import tech.easyflow.ai.entity.Workflow;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.math.BigInteger;

/**
 * {@link WorkflowTool} 单元测试。
 *
 * @author Codex
 * @since 2026-05-11
 */
public class WorkflowToolTest {

    /**
     * 启用英文名且字段合法时，应直接使用英文名。
     *
     * @throws Exception 反射注入异常
     */
    @Test
    public void shouldUseValidEnglishNameWhenEnabled() throws Exception {
        ApplicationContext previousContext = getStaticField("applicationContext");
        Object previousBeanFactory = getStaticField("beanFactory");
        try {
            setStaticField("beanFactory", null);
            setStaticField("applicationContext", mockApplicationContext(buildChainExecutor("workflow-1")));

            WorkflowTool tool = new WorkflowTool(buildWorkflow(101, "workflow_search", "工作流检索"), true, "workflow-1");

            Assert.assertEquals("workflow_search", tool.getName());
        } finally {
            setStaticField("applicationContext", previousContext);
            setStaticField("beanFactory", previousBeanFactory);
        }
    }

    /**
     * 启用英文名但字段缺失时，应回退为稳定安全名称。
     *
     * @throws Exception 反射注入异常
     */
    @Test
    public void shouldFallbackToSafeNameWhenEnglishNameMissing() throws Exception {
        ApplicationContext previousContext = getStaticField("applicationContext");
        Object previousBeanFactory = getStaticField("beanFactory");
        try {
            setStaticField("beanFactory", null);
            setStaticField("applicationContext", mockApplicationContext(buildChainExecutor("workflow-2")));

            WorkflowTool tool = new WorkflowTool(buildWorkflow(202, null, "工作流检索"), true, "workflow-2");

            Assert.assertEquals("workflow_202", tool.getName());
        } finally {
            setStaticField("applicationContext", previousContext);
            setStaticField("beanFactory", previousBeanFactory);
        }
    }

    /**
     * 启用英文名但字段非法时，应回退为稳定安全名称。
     *
     * @throws Exception 反射注入异常
     */
    @Test
    public void shouldFallbackToSafeNameWhenEnglishNameInvalid() throws Exception {
        ApplicationContext previousContext = getStaticField("applicationContext");
        Object previousBeanFactory = getStaticField("beanFactory");
        try {
            setStaticField("beanFactory", null);
            setStaticField("applicationContext", mockApplicationContext(buildChainExecutor("workflow-3")));

            WorkflowTool tool = new WorkflowTool(buildWorkflow(303, "工作流-A", "工作流检索"), true, "workflow-3");

            Assert.assertEquals("workflow_303", tool.getName());
        } finally {
            setStaticField("applicationContext", previousContext);
            setStaticField("beanFactory", previousBeanFactory);
        }
    }

    private Workflow buildWorkflow(long id, String englishName, String title) {
        Workflow workflow = new Workflow();
        workflow.setId(BigInteger.valueOf(id));
        workflow.setEnglishName(englishName);
        workflow.setTitle(title);
        workflow.setDescription("desc-" + id);
        return workflow;
    }

    private ChainExecutor buildChainExecutor(String definitionId) {
        ChainDefinitionRepository definitionRepository = id -> {
            ChainDefinition definition = new ChainDefinition();
            definition.setId(definitionId);
            return definition;
        };
        return new ChainExecutor(
                definitionRepository,
                new InMemoryChainStateRepository(),
                new InMemoryNodeStateRepository()
        );
    }

    private ApplicationContext mockApplicationContext(ChainExecutor chainExecutor) {
        return (ApplicationContext) Proxy.newProxyInstance(
                ApplicationContext.class.getClassLoader(),
                new Class[]{ApplicationContext.class},
                (proxy, method, args) -> {
                    if ("getBean".equals(method.getName()) && args != null && args.length == 1 && args[0] == ChainExecutor.class) {
                        return chainExecutor;
                    }
                    if ("equals".equals(method.getName())) {
                        return proxy == args[0];
                    }
                    if ("hashCode".equals(method.getName())) {
                        return System.identityHashCode(proxy);
                    }
                    return defaultValue(method.getReturnType());
                }
        );
    }

    private static <T> T getStaticField(String name) throws Exception {
        Field field = Class.forName("tech.easyflow.common.util.SpringContextUtil").getDeclaredField(name);
        field.setAccessible(true);
        @SuppressWarnings("unchecked")
        T value = (T) field.get(null);
        return value;
    }

    private static void setStaticField(String name, Object value) throws Exception {
        Field field = Class.forName("tech.easyflow.common.util.SpringContextUtil").getDeclaredField(name);
        field.setAccessible(true);
        field.set(null, value);
    }

    private Object defaultValue(Class<?> returnType) {
        if (returnType == null || returnType == Void.TYPE) {
            return null;
        }
        if (!returnType.isPrimitive()) {
            return null;
        }
        if (returnType == Boolean.TYPE) {
            return false;
        }
        if (returnType == Character.TYPE) {
            return '\0';
        }
        if (returnType == Byte.TYPE) {
            return (byte) 0;
        }
        if (returnType == Short.TYPE) {
            return (short) 0;
        }
        if (returnType == Integer.TYPE) {
            return 0;
        }
        if (returnType == Long.TYPE) {
            return 0L;
        }
        if (returnType == Float.TYPE) {
            return 0F;
        }
        if (returnType == Double.TYPE) {
            return 0D;
        }
        return null;
    }
}
