package tech.easyflow.admin.controller.ai;

import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import jakarta.servlet.http.HttpServletRequest;
import org.mockito.MockedStatic;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.testng.Assert;
import org.testng.annotations.Test;
import tech.easyflow.admin.service.ai.WorkflowChatEventStream;
import tech.easyflow.ai.easyagentsflow.service.WorkflowCheckService;
import tech.easyflow.ai.easyagentsflow.service.WorkflowRunningParameterResolver;
import tech.easyflow.ai.easyagentsflow.support.PublishedWorkflowDefinitionIds;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.service.WorkflowExecResultService;
import tech.easyflow.ai.service.WorkflowExecStepService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.ai.service.WorkflowShareService;
import tech.easyflow.ai.share.WorkflowSharePolicy;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.service.ResourceAccessService;

import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;

import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * {@link WorkflowChatController} 运行来源与分享边界测试。
 */
public class WorkflowChatControllerTest {

    /**
     * 验证登录用户可以进入并运行未发布工作流，同时描述信息禁止分享。
     */
    @Test
    public void shouldRunDraftForAuthenticatedRequestWithoutSharing() {
        ControllerFixture fixture = fixture(workflow(PublishStatus.DRAFT, "draft-content", false));
        when(fixture.parameterResolver.buildRunningParametersView(fixture.current))
                .thenReturn(new LinkedHashMap<>());
        when(fixture.parameterResolver.normalizeRuntimeVariables(
                eq("draft-content"),
                anyMap()
        )).thenReturn(new LinkedHashMap<>());
        when(fixture.eventStream.start(eq("1"), anyMap())).thenReturn(new SseEmitter());

        try (MockedStatic<SaTokenUtil> login = login(fixture.account)) {
            Result<Map<String, Object>> descriptor = fixture.controller.descriptor(
                    BigInteger.ONE,
                    request(Map.of())
            );
            fixture.controller.run(BigInteger.ONE, Map.of(), request(Map.of()));

            Assert.assertEquals(descriptor.getData().get("shareable"), false);
            Assert.assertEquals(
                    descriptor.getData().get("publishStatus"),
                    PublishStatus.DRAFT.getCode()
            );
        }

        verify(fixture.parameterResolver).buildRunningParametersView(fixture.current);
        verify(fixture.eventStream).start(eq("1"), anyMap());
    }

    /**
     * 验证已发布工作流的管理端运行继续读取发布快照。
     */
    @Test
    public void shouldRunPublishedSnapshotForAuthenticatedRequest() {
        Workflow current = workflow(PublishStatus.PUBLISHED, "draft-content", true);
        Workflow published = workflow(PublishStatus.PUBLISHED, "published-content", true);
        ControllerFixture fixture = fixture(current, published);
        when(fixture.parameterResolver.buildRunningParametersView(published))
                .thenReturn(new LinkedHashMap<>());
        when(fixture.parameterResolver.normalizeRuntimeVariables(
                eq("published-content"),
                anyMap()
        )).thenReturn(new LinkedHashMap<>());
        when(fixture.eventStream.start(
                eq(PublishedWorkflowDefinitionIds.published("1")),
                anyMap()
        )).thenReturn(new SseEmitter());

        try (MockedStatic<SaTokenUtil> login = login(fixture.account)) {
            Result<Map<String, Object>> descriptor = fixture.controller.descriptor(
                    BigInteger.ONE,
                    request(Map.of())
            );
            fixture.controller.run(BigInteger.ONE, Map.of(), request(Map.of()));

            Assert.assertEquals(descriptor.getData().get("shareable"), true);
        }

        verify(fixture.parameterResolver).buildRunningParametersView(published);
        verify(fixture.eventStream).start(
                eq(PublishedWorkflowDefinitionIds.published("1")),
                anyMap()
        );
    }

    /**
     * 验证分享访问仍拒绝未发布工作流。
     */
    @Test
    public void shouldRejectDraftWorkflowFromShareRequest() {
        ControllerFixture fixture = fixture(workflow(PublishStatus.DRAFT, "draft-content", false));
        HttpServletRequest request = request(Map.of(
                WorkflowSharePolicy.CHAT_SHARE_KEY_HEADER.toLowerCase(Locale.ROOT),
                "share-key"
        ));

        try (MockedStatic<SaTokenUtil> login = login(fixture.account)) {
            Assert.expectThrows(
                    BusinessException.class,
                    () -> fixture.controller.descriptor(BigInteger.ONE, request)
            );
        }

        verify(fixture.workflowShareService).assertChatShareAccess(
                "share-key",
                BigInteger.ONE,
                BigInteger.ONE
        );
        verify(fixture.resourceAccessService, never()).assertAccess(
                org.mockito.ArgumentMatchers.any(),
                org.mockito.ArgumentMatchers.any(),
                org.mockito.ArgumentMatchers.any(),
                org.mockito.ArgumentMatchers.anyString()
        );
    }

    /**
     * 创建使用同一当前视图和发布视图的测试夹具。
     *
     * @param current 当前工作流
     * @return 控制器测试夹具
     */
    private ControllerFixture fixture(Workflow current) {
        return fixture(current, current);
    }

    /**
     * 创建控制器测试夹具。
     *
     * @param current 当前工作流
     * @param published 发布工作流视图
     * @return 控制器测试夹具
     */
    private ControllerFixture fixture(Workflow current, Workflow published) {
        WorkflowService workflowService = mock(WorkflowService.class);
        WorkflowShareService workflowShareService = mock(WorkflowShareService.class);
        WorkflowCheckService workflowCheckService = mock(WorkflowCheckService.class);
        WorkflowRunningParameterResolver parameterResolver =
                mock(WorkflowRunningParameterResolver.class);
        ResourceAccessService resourceAccessService = mock(ResourceAccessService.class);
        WorkflowChatEventStream eventStream = mock(WorkflowChatEventStream.class);
        WorkflowChatController controller = new WorkflowChatController();
        setField(controller, "workflowService", workflowService);
        setField(controller, "workflowShareService", workflowShareService);
        setField(controller, "workflowCheckService", workflowCheckService);
        setField(controller, "parameterResolver", parameterResolver);
        setField(controller, "resourceAccessService", resourceAccessService);
        setField(controller, "eventStream", eventStream);
        setField(controller, "chainExecutor", mock(ChainExecutor.class));
        setField(controller, "execResultService", mock(WorkflowExecResultService.class));
        setField(controller, "execStepService", mock(WorkflowExecStepService.class));
        when(workflowService.getById(BigInteger.ONE)).thenReturn(current);
        when(workflowService.getPublishedById(BigInteger.ONE)).thenReturn(published);

        LoginAccount account = new LoginAccount();
        account.setId(BigInteger.ONE);
        account.setTenantId(BigInteger.ONE);
        return new ControllerFixture(
                controller,
                current,
                workflowService,
                workflowShareService,
                parameterResolver,
                resourceAccessService,
                eventStream,
                account
        );
    }

    /**
     * 创建工作流测试视图。
     *
     * @param publishStatus 发布状态
     * @param content 工作流内容
     * @param withSnapshot 是否包含发布快照
     * @return 工作流测试视图
     */
    private Workflow workflow(
            PublishStatus publishStatus,
            String content,
            boolean withSnapshot
    ) {
        Workflow workflow = new Workflow();
        workflow.setId(BigInteger.ONE);
        workflow.setContent(content);
        workflow.setPublishStatus(publishStatus.getCode());
        if (withSnapshot) {
            workflow.setPublishedSnapshotJson(Map.of("content", content));
        }
        return workflow;
    }

    /**
     * 创建登录账号静态模拟。
     *
     * @param account 登录账号
     * @return 静态模拟句柄
     */
    private MockedStatic<SaTokenUtil> login(LoginAccount account) {
        MockedStatic<SaTokenUtil> login = mockStatic(SaTokenUtil.class);
        login.when(SaTokenUtil::getLoginAccount).thenReturn(account);
        return login;
    }

    /**
     * 创建仅提供请求头能力的轻量 Servlet 请求代理。
     *
     * @param headers 小写请求头映射
     * @return HTTP 请求代理
     */
    private HttpServletRequest request(Map<String, String> headers) {
        return (HttpServletRequest) java.lang.reflect.Proxy.newProxyInstance(
                getClass().getClassLoader(),
                new Class<?>[]{HttpServletRequest.class},
                (proxy, method, args) -> {
                    if ("getHeader".equals(method.getName())) {
                        String name = String.valueOf(args[0]).toLowerCase(Locale.ROOT);
                        return headers.get(name);
                    }
                    return defaultValue(method.getReturnType());
                }
        );
    }

    /**
     * 通过反射设置控制器依赖。
     *
     * @param target 目标对象
     * @param fieldName 字段名
     * @param value 字段值
     */
    private void setField(Object target, String fieldName, Object value) {
        try {
            Field field = target.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(target, value);
        } catch (ReflectiveOperationException exception) {
            throw new IllegalStateException("设置测试字段失败: " + fieldName, exception);
        }
    }

    /**
     * 返回代理方法所需的基础类型默认值。
     *
     * @param returnType 返回类型
     * @return 默认值
     */
    private Object defaultValue(Class<?> returnType) {
        if (!returnType.isPrimitive()) {
            return null;
        }
        if (boolean.class == returnType) {
            return false;
        }
        if (char.class == returnType) {
            return '\0';
        }
        if (byte.class == returnType) {
            return (byte) 0;
        }
        if (short.class == returnType) {
            return (short) 0;
        }
        if (int.class == returnType) {
            return 0;
        }
        if (long.class == returnType) {
            return 0L;
        }
        if (float.class == returnType) {
            return 0F;
        }
        return 0D;
    }

    /**
     * 控制器及其测试依赖夹具。
     *
     * @param controller 控制器
     * @param current 当前工作流
     * @param workflowService 工作流服务
     * @param workflowShareService 工作流分享服务
     * @param parameterResolver 参数解析器
     * @param resourceAccessService 资源权限服务
     * @param eventStream 事件流服务
     * @param account 登录账号
     */
    private record ControllerFixture(
            WorkflowChatController controller,
            Workflow current,
            WorkflowService workflowService,
            WorkflowShareService workflowShareService,
            WorkflowRunningParameterResolver parameterResolver,
            ResourceAccessService resourceAccessService,
            WorkflowChatEventStream eventStream,
            LoginAccount account
    ) {
    }
}
