package tech.easyflow.publicapi.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import tech.easyflow.agent.runtime.AgentChatRequest;
import tech.easyflow.agent.runtime.AgentRunService;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.entity.SysApiKey;
import tech.easyflow.system.service.SysApiKeyService;

import java.lang.reflect.Proxy;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * {@link PublicAgentController} API Key 身份边界测试。
 */
public class PublicAgentControllerTest {

    /**
     * 验证缺少 API Key 时返回明确的 HTTP 401 业务异常。
     */
    @Test
    public void chatShouldRejectMissingApiKey() {
        PublicAgentController controller = new PublicAgentController(
                new RecordingAgentRunService(new ArrayList<>()),
                proxy(
                        SysApiKeyService.class,
                        (instance, method, args) -> {
                            throw new AssertionError("缺少 API Key 时不应调用服务");
                        }
                )
        );
        HttpServletRequest request = proxy(
                HttpServletRequest.class,
                (instance, method, args) -> {
                    if ("getHeader".equals(method.getName())) {
                        return " ";
                    }
                    throw new AssertionError("测试路径不应调用 HttpServletRequest." + method.getName());
                }
        );

        BusinessException exception = Assert.assertThrows(
                BusinessException.class,
                () -> controller.chat(new AgentChatRequest(), request)
        );

        Assert.assertEquals(401, exception.getHttpStatus());
        Assert.assertEquals("Apikey不能为空!", exception.getMessage());
    }

    /**
     * 验证接口先校验 API Key，并将其租户与部门传入 Agent 运行时。
     */
    @Test
    public void chatShouldAuthorizeBeforeRunningWithApiKeyTenant() {
        List<String> calls = new ArrayList<>();
        SysApiKey apiKey = new SysApiKey();
        apiKey.setId(BigInteger.valueOf(101));
        apiKey.setTenantId(BigInteger.valueOf(201));
        apiKey.setDeptId(BigInteger.valueOf(301));
        SysApiKeyService apiKeyService = proxy(
                SysApiKeyService.class,
                (instance, method, args) -> {
                    if ("checkApikeyPermission".equals(method.getName())) {
                        calls.add("permission");
                        Assert.assertEquals("test-key", args[0]);
                        Assert.assertEquals("/public-api/agent/chat", args[1]);
                        return null;
                    }
                    if ("getSysApiKey".equals(method.getName())) {
                        calls.add("load");
                        return apiKey;
                    }
                    throw new AssertionError("测试路径不应调用 SysApiKeyService." + method.getName());
                }
        );
        RecordingAgentRunService runService = new RecordingAgentRunService(calls);
        PublicAgentController controller = new PublicAgentController(runService, apiKeyService);
        HttpServletRequest request = proxy(
                HttpServletRequest.class,
                (instance, method, args) -> {
                    if ("getHeader".equals(method.getName())) {
                        return "test-key";
                    }
                    if ("getRequestURI".equals(method.getName())) {
                        return "/public-api/agent/chat";
                    }
                    throw new AssertionError("测试路径不应调用 HttpServletRequest." + method.getName());
                }
        );

        SseEmitter result = controller.chat(new AgentChatRequest(), request);

        Assert.assertSame(runService.emitter, result);
        Assert.assertEquals(List.of("permission", "load", "chat"), calls);
        Assert.assertEquals(apiKey.getId(), runService.account.getId());
        Assert.assertEquals(apiKey.getTenantId(), runService.account.getTenantId());
        Assert.assertEquals(apiKey.getDeptId(), runService.account.getDeptId());
    }

    /**
     * 创建接口代理。
     *
     * @param type 接口类型
     * @param handler 调用处理器
     * @param <T> 接口类型
     * @return 代理实例
     */
    private <T> T proxy(Class<T> type, java.lang.reflect.InvocationHandler handler) {
        return type.cast(Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, handler));
    }

    /**
     * 记录公共 Agent 调用身份的运行服务。
     */
    private static class RecordingAgentRunService extends AgentRunService {

        private final List<String> calls;
        private final SseEmitter emitter = new SseEmitter();
        private LoginAccount account;

        /**
         * 创建运行服务桩。
         *
         * @param calls 调用顺序记录
         */
        private RecordingAgentRunService(List<String> calls) {
            this.calls = calls;
        }

        /**
         * 记录 API Key 调用身份。
         *
         * @param chatRequest 聊天请求
         * @param apiAccount API Key 调用身份
         * @return 测试用 SSE Emitter
         */
        @Override
        public SseEmitter chatPublic(AgentChatRequest chatRequest, LoginAccount apiAccount) {
            calls.add("chat");
            account = apiAccount;
            return emitter;
        }
    }
}
