package com.sino.mci.inbound.engine;

import com.jayway.jsonpath.JsonPath;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.UUID;

import static com.sino.mci.test.AuthTestHelper.createTenantAndLogin;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * 入站消息端到端测试。
 *
 * <p>覆盖完整业务链路：创建租户并配置入站 webhook → 创建并发布入站流程 →
 * 接收消息事件 → 上下文解析 → 意图识别 → webhook 推送（RestTemplate 被 mock）。</p>
 */
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(properties = {
        "spring.rabbitmq.listener.simple.auto-startup=false",
        "spring.main.allow-bean-definition-overriding=true"
})
@Transactional
class InboundEndToEndTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private RestTemplate restTemplate;

    @Test
    void shouldPushInboundMessageToBusinessWebhook() throws Exception {
        String tenantCode = "inbound-e2e-" + UUID.randomUUID().toString().substring(0, 8);
        String authToken = createTenantAndLogin(mockMvc, tenantCode, "入站端到端测试");
        String webhookUrl = "https://example.com/inbound-webhook";

        // 1. 配置租户入站 webhook 与签名密钥
        String webhookConfig = String.format(
                "{\"inbound_webhook_url\":\"%s\",\"auth_config\":{\"sign_algorithm\":\"hmac-sha256\",\"webhook_secret\":\"e2e-secret\"}}",
                webhookUrl);
        mockMvc.perform(post("/msg-platform/api/v1/account/tenant/webhook")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(webhookConfig))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0));

        // 2. 创建入站流程：receive -> intent -> webhook
        String flowPayload = """
                {
                  "flowCode": "inbound_default",
                  "flowName": "E2E Inbound",
                  "flowType": "inbound",
                  "definitionJson": {
                    "nodes": [
                      {"id": "n1", "type": "receive", "config": {"channel_type": "wecom"}},
                      {"id": "n2", "type": "intent"},
                      {"id": "n3", "type": "webhook"}
                    ],
                    "edges": [
                      {"source": "n1", "target": "n2"},
                      {"source": "n2", "target": "n3"}
                    ]
                  }
                }
                """;
        mockMvc.perform(post("/msg-platform/api/v1/inbound/flow")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(flowPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0));

        // 3. 发布流程
        mockMvc.perform(post("/msg-platform/api/v1/inbound/flow/inbound_default/publish")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0));

        when(restTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class)))
                .thenReturn(ResponseEntity.ok("ok"));

        // 4. 接收入站消息
        String eventId = "evt-" + UUID.randomUUID().toString().substring(0, 8);
        String messagePayload = String.format(
                "{\"eventId\":\"%s\",\"channelType\":\"wecom\",\"eventType\":\"message\",\"messageSource\":\"realtime\",\"conversationId\":1,\"personId\":1,\"content\":{\"type\":\"text\",\"body\":\"我要救援，客户张三，地址北京\"}}",
                eventId);
        String receiveResponse = mockMvc.perform(post("/msg-platform/api/v1/inbound/receive")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(messagePayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data.eventId").value(eventId))
                .andReturn().getResponse().getContentAsString();
        Integer isProcessed = JsonPath.read(receiveResponse, "$.data.isProcessed");
        org.junit.jupiter.api.Assertions.assertEquals(1, isProcessed);

        // 5. 验证 webhook 被推送到业务系统
        verify(restTemplate, atLeastOnce()).exchange(eq(webhookUrl), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class));
    }

    @TestConfiguration
    static class MockConfig {

        @Bean
        public RestTemplate restTemplate() {
            return mock(RestTemplate.class);
        }
    }
}
