package cn.nianxx.thhotel.integrations.mcp.superagent.control;

import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import cn.nianxx.thhotel.ThHotelApplication;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;

@SpringBootTest(
        classes = ThHotelApplication.class,
        properties = {
                "mcp.enabled=true",
                "mcp.auth-token=test-mcp-token",
                "mcp.enable-submit-task-results=false",
                "mcp.max-body-bytes=12000"
        })
@AutoConfigureMockMvc
@ActiveProfiles("test")
class SuperAgentMcpControllerTest {

    private static final String ENDPOINT = "/mcp";
    private static final String AUTHORIZATION = "Bearer test-mcp-token";

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldRejectMcpRequestWithoutBearerToken() throws Exception {
        String body = """
                {
                  "jsonrpc": "2.0",
                  "id": "mcp-auth-001",
                  "method": "tools/list",
                  "params": {}
                }
                """;

        mockMvc.perform(post(ENDPOINT)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(body))
                .andExpect(status().isUnauthorized())
                .andExpect(jsonPath("$.jsonrpc").value("2.0"))
                .andExpect(jsonPath("$.error.data.code").value("MCP_AUTH_INVALID"));
    }

    @Test
    void shouldReturnJsonRpcParseErrorWhenRequestBodyInvalid() throws Exception {
        mockMvc.perform(post(ENDPOINT)
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", AUTHORIZATION)
                        .content("{invalid-json"))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.jsonrpc").value("2.0"))
                .andExpect(jsonPath("$.error.code").value(-32700))
                .andExpect(jsonPath("$.error.data.code").value("MCP_REQUEST_INVALID"));
    }

    @Test
    void shouldRejectMcpRequestWhenBodyLargerThanConfiguredLimit() throws Exception {
        String body = """
                {
                  "jsonrpc": "2.0",
                  "id": "mcp-body-too-large-001",
                  "method": "tools/list",
                  "params": {
                    "padding": "%s"
                  }
                }
                """.formatted("x".repeat(12_100));

        mockMvc.perform(post(ENDPOINT)
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", AUTHORIZATION)
                        .content(body))
                .andExpect(status().isPayloadTooLarge())
                .andExpect(jsonPath("$.jsonrpc").value("2.0"))
                .andExpect(jsonPath("$.error.data.code").value("MCP_REQUEST_BODY_TOO_LARGE"));
    }

    @Test
    void shouldAcceptInitializedNotificationWithoutJsonRpcResponse() throws Exception {
        String body = """
                {
                  "jsonrpc": "2.0",
                  "method": "notifications/initialized",
                  "params": {}
                }
                """;

        mockMvc.perform(post(ENDPOINT)
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", AUTHORIZATION)
                        .content(body))
                .andExpect(status().isAccepted())
                .andExpect(content().string(""));
    }

    @Test
    void shouldListFiveSuperAgentMcpTools() throws Exception {
        String body = """
                {
                  "jsonrpc": "2.0",
                  "id": "mcp-tools-001",
                  "method": "tools/list",
                  "params": {}
                }
                """;

        mockMvc.perform(post(ENDPOINT)
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", AUTHORIZATION)
                        .content(body))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.jsonrpc").value("2.0"))
                .andExpect(jsonPath("$.id").value("mcp-tools-001"))
                .andExpect(jsonPath("$.result.tools.length()").value(5))
                .andExpect(jsonPath("$.result.tools[0].name").value("th_hotel_query_case_context"))
                .andExpect(jsonPath("$.result.tools[0].inputSchema.required.length()").value(0))
                .andExpect(jsonPath("$.result.tools[0].annotations.readOnlyHint").value(true))
                .andExpect(jsonPath("$.result.tools[3].name").value("th_hotel_list_message_conversation_messages"))
                .andExpect(jsonPath("$.result.tools[3].inputSchema.required.length()").value(0))
                .andExpect(jsonPath("$.result.tools[3].annotations.readOnlyHint").value(true))
                .andExpect(jsonPath("$.result.tools[4].name").value("th_hotel_submit_task_results"))
                .andExpect(jsonPath("$.result.tools[4].inputSchema.required[0]").value("source_message_id"))
                .andExpect(jsonPath("$.result.tools[4].inputSchema.required[1]").value("ai_task_results"))
                .andExpect(jsonPath("$.result.tools[4].inputSchema.properties.source_provider.description")
                        .value(containsString("兼容字段")))
                .andExpect(jsonPath("$.result.tools[4].inputSchema.properties.source_channel.description")
                        .value(containsString("OUTLOOK")))
                .andExpect(jsonPath("$.result.tools[4].annotations.readOnlyHint").value(false))
                .andExpect(jsonPath("$.result.tools[4].annotations.destructiveHint").value(true));
    }

    @Test
    void shouldCallCaseContextToolThroughEmbeddedMcpEndpoint() throws Exception {
        String body = """
                {
                  "jsonrpc": "2.0",
                  "id": "mcp-call-case-context-001",
                  "method": "tools/call",
                  "params": {
                    "name": "th_hotel_query_case_context",
                    "arguments": {
                      "group_code": "GRP-MCP-NOT-FOUND"
                    }
                  }
                }
                """;

        mockMvc.perform(post(ENDPOINT)
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", AUTHORIZATION)
                        .content(body))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.jsonrpc").value("2.0"))
                .andExpect(jsonPath("$.id").value("mcp-call-case-context-001"))
                .andExpect(jsonPath("$.result.isError").value(false))
                .andExpect(jsonPath("$.result.structuredContent.success").value(true))
                .andExpect(jsonPath("$.result.structuredContent.data.matched_order_records.length()").value(0))
                .andExpect(jsonPath("$.result.structuredContent.data.target_object_validation.status").value("none"))
                .andExpect(content().string(containsString("th_hotel_query_case_context")));
    }

    @Test
    void shouldRejectSubmitTaskResultsToolWhenWriteToolDisabled() throws Exception {
        String body = """
                {
                  "jsonrpc": "2.0",
                  "id": "mcp-submit-disabled-001",
                  "method": "tools/call",
                  "params": {
                    "name": "th_hotel_submit_task_results",
                    "arguments": {
                      "source_message_id": "mail-mcp-disabled-001",
                      "ai_task_results": []
                    }
                  }
                }
                """;

        mockMvc.perform(post(ENDPOINT)
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", AUTHORIZATION)
                        .content(body))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.jsonrpc").value("2.0"))
                .andExpect(jsonPath("$.id").value("mcp-submit-disabled-001"))
                .andExpect(jsonPath("$.result.isError").value(true))
                .andExpect(jsonPath("$.result.structuredContent.error.code").value("MCP_TOOL_DISABLED"));
    }
}
