package tech.easyflow.publicapi.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.publicapi.dto.PublicWorkflowChainStatus;
import tech.easyflow.publicapi.dto.PublicWorkflowExecutionStatus;
import tech.easyflow.publicapi.dto.PublicWorkflowInfo;
import tech.easyflow.publicapi.dto.PublicWorkflowRunResult;
import tech.easyflow.publicapi.dto.PublicWorkflowTopology;

import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * {@link PublicWorkflowController} HTTP 响应契约测试。
 */
public class PublicWorkflowControllerContractTest {

    /**
     * 验证工作流公共 API 明确返回 JSON，避免通配 Accept 请求被 CBOR 转换器接管。
     */
    @Test
    public void shouldDeclareJsonResponseMediaType() {
        RequestMapping requestMapping = PublicWorkflowController.class.getAnnotation(RequestMapping.class);

        Assert.assertNotNull(requestMapping);
        Assert.assertArrayEquals(
                new String[]{MediaType.APPLICATION_JSON_VALUE},
                requestMapping.produces()
        );
    }

    /**
     * 验证 multipart 调用复用 runAsync 路径并明确声明媒体类型。
     */
    @Test
    public void shouldExposeMultipartRunAsyncOnCompatiblePath() {
        Method multipartMethod = Arrays.stream(
                        PublicWorkflowController.class.getDeclaredMethods())
                .filter(method -> "runAsyncMultipart".equals(
                        method.getName()))
                .findFirst()
                .orElseThrow();
        PostMapping postMapping =
                multipartMethod.getAnnotation(PostMapping.class);

        Assert.assertNotNull(postMapping);
        Assert.assertArrayEquals(
                new String[]{"/runAsync"},
                postMapping.value());
        Assert.assertArrayEquals(
                new String[]{MediaType.MULTIPART_FORM_DATA_VALUE},
                postMapping.consumes());
    }

    /**
     * 验证 JSON 调用显式声明媒体类型，避免与 Multipart 路由混淆。
     */
    @Test
    public void shouldExposeJsonRunAsyncWithExplicitMediaType() {
        Method jsonMethod = Arrays.stream(
                        PublicWorkflowController.class.getDeclaredMethods())
                .filter(method -> "runAsync".equals(method.getName()))
                .findFirst()
                .orElseThrow();
        PostMapping postMapping =
                jsonMethod.getAnnotation(PostMapping.class);

        Assert.assertNotNull(postMapping);
        Assert.assertArrayEquals(
                new String[]{MediaType.APPLICATION_JSON_VALUE},
                postMapping.consumes());
    }

    /**
     * 验证富响应继续把执行 ID 保存在 data 字符串中。
     *
     * @throws Exception JSON 序列化失败
     */
    @Test
    public void runResultShouldKeepLegacyExecuteIdData()
            throws Exception {
        PublicWorkflowTopology topology =
                new PublicWorkflowTopology(
                        "1",
                        null,
                        "测试工作流",
                        null,
                        1,
                        null,
                        List.of(),
                        List.of(),
                        List.of(),
                        List.of(),
                        false,
                        List.of());

        PublicWorkflowRunResult result =
                PublicWorkflowRunResult.success(
                        "execution-1",
                        topology);

        Assert.assertEquals("execution-1", result.getData());
        Assert.assertSame(topology, result.getWorkflow());
        Assert.assertEquals(0, result.getErrorCode());
        String json = new ObjectMapper().writeValueAsString(result);
        Assert.assertTrue(json.contains("\"data\":\"execution-1\""));
        Assert.assertTrue(json.contains("\"workflow\""));
    }

    /**
     * 验证详情接口 DTO 只公开调用所需的基础字段。
     *
     * @throws Exception JSON 序列化失败
     */
    @Test
    public void workflowInfoShouldExcludeInternalFields()
            throws Exception {
        Workflow workflow = new Workflow();
        workflow.setId(new BigInteger("9007199254740993"));
        workflow.setAlias("document-parser");
        workflow.setTitle("文档解析");
        workflow.setContent("internal-content");
        workflow.setTenantId(BigInteger.TEN);
        workflow.setDeptId(BigInteger.ONE);
        workflow.setPublishedSnapshotJson(
                Map.of("secret", "snapshot"));

        String json = new ObjectMapper().writeValueAsString(
                PublicWorkflowInfo.from(workflow));

        Assert.assertTrue(json.contains(
                "\"id\":\"9007199254740993\""));
        Assert.assertTrue(json.contains(
                "\"alias\":\"document-parser\""));
        Assert.assertFalse(json.contains("content"));
        Assert.assertFalse(json.contains("tenantId"));
        Assert.assertFalse(json.contains("deptId"));
        Assert.assertFalse(json.contains("publishedSnapshotJson"));
        Assert.assertFalse(json.contains("secret"));
    }

    /**
     * 验证链路状态以小写可读枚举返回，不再公开内部数值。
     *
     * @throws Exception JSON 序列化失败
     */
    @Test
    public void chainStatusShouldSerializeReadableEnum()
            throws Exception {
        PublicWorkflowChainStatus status =
                new PublicWorkflowChainStatus(
                        "execute-1",
                        PublicWorkflowExecutionStatus.DONE,
                        true,
                        null,
                        Map.of("output", "ok"),
                        Map.of(),
                        null);

        String json = new ObjectMapper().writeValueAsString(status);

        Assert.assertTrue(json.contains("\"status\":\"done\""));
        Assert.assertTrue(json.contains("\"terminal\":true"));
        Assert.assertFalse(json.contains("\"status\":20"));
    }
}
