package com.sino.mci.send.controller;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;

import static com.sino.mci.test.AuthTestHelper.createTenantAndLogin;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
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;

@SpringBootTest
@AutoConfigureMockMvc
@Transactional
public class SendControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldTestSendToConversationAndQueryRecords() throws Exception {
        String tenantCode = "send-" + UUID.randomUUID().toString().substring(0, 8);
        String authToken = createTenantAndLogin(mockMvc, tenantCode, "发送测试");

        String accountPayload = "{\"accountType\":1,\"channelType\":\"wecom\",\"accountId\":\"ZhangSan\",\"accountName\":\"张三\"}";
        String accountResponse = mockMvc.perform(post("/msg-platform/api/v1/channel-account")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(accountPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andReturn().getResponse().getContentAsString();
        Long accountId = ((Number) com.jayway.jsonpath.JsonPath.read(accountResponse, "$.data.id")).longValue();

        String conversationPayload = "{\"conversationType\":2,\"channelType\":\"wecom\",\"channelConversationId\":\"room-001\",\"conversationName\":\"测试群\"}";
        String conversationResponse = mockMvc.perform(post("/msg-platform/api/v1/conversation")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(conversationPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andReturn().getResponse().getContentAsString();
        Long conversationId = ((Number) com.jayway.jsonpath.JsonPath.read(conversationResponse, "$.data.id")).longValue();

        String bindPayload = String.format("{\"accountId\":%d,\"bindingType\":1,\"sortOrder\":1}", accountId);
        mockMvc.perform(post("/msg-platform/api/v1/conversation/" + conversationId + "/bind-account")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(bindPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0));

        String sendPayload = String.format(
                "{\"targetType\":\"conversation\",\"targetValue\":{\"conversation_id\":%d},\"contentType\":\"text\",\"content\":{\"text\":\"hello\"},\"channelStrategy\":{\"channel_type\":\"wecom\",\"strategy\":\"primary\"}}",
                conversationId);
        String sendResponse = mockMvc.perform(post("/msg-platform/api/v1/send/test")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(sendPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data.totalCount").value(1))
                .andExpect(jsonPath("$.data.status").value("simulated"))
                .andReturn().getResponse().getContentAsString();
        String taskId = com.jayway.jsonpath.JsonPath.read(sendResponse, "$.data.taskId");

        mockMvc.perform(get("/msg-platform/api/v1/send/" + taskId + "/records")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data.length()").value(1))
                .andExpect(jsonPath("$.data[0].sendStatus").value(2))
                .andExpect(jsonPath("$.data[0].channelType").value("wecom"));
    }

    @Test
    void shouldRejectEmptyRuleTarget() throws Exception {
        String tenantCode = "send-" + UUID.randomUUID().toString().substring(0, 8);
        String authToken = createTenantAndLogin(mockMvc, tenantCode, "发送测试");

        String sendPayload = "{\"targetType\":\"rule\",\"targetValue\":{},\"contentType\":\"text\",\"content\":{\"text\":\"hello\"}}";
        mockMvc.perform(post("/msg-platform/api/v1/send/test")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(sendPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(-1));
    }

    @Test
    void shouldResolveRuleTargetWithAndLogic() throws Exception {
        String tenantCode = "send-" + UUID.randomUUID().toString().substring(0, 8);
        String authToken = createTenantAndLogin(mockMvc, tenantCode, "发送测试");

        Long accountId = createChannelAccount(authToken, tenantCode);
        Long tagId = createTag(authToken, tenantCode, "rule-and");
        Long personId = createPerson(authToken, tenantCode, 3);
        bindPersonTag(authToken, tenantCode, personId, tagId);
        createPersonIdentity(authToken, tenantCode, personId, "wecom", "user-001");
        Long conversationId = createConversation(authToken, tenantCode, "wecom", "room-and-001");
        bindConversationTag(authToken, tenantCode, conversationId, tagId);
        bindConversationAccount(authToken, tenantCode, conversationId, accountId);

        String sendPayload = String.format(
                "{\"targetType\":\"rule\",\"targetValue\":{\"rules\":[{\"field\":\"tag\",\"operator\":\"in\",\"value\":[%d]},{\"field\":\"person_type\",\"operator\":\"eq\",\"value\":3},{\"field\":\"channel_type\",\"operator\":\"eq\",\"value\":\"wecom\"}],\"logic\":\"and\"},\"contentType\":\"text\",\"content\":{\"text\":\"hello\"},\"channelStrategy\":{\"channel_type\":\"wecom\",\"strategy\":\"primary\"}}",
                tagId);
        mockMvc.perform(post("/msg-platform/api/v1/send/test")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(sendPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data.totalCount").value(2))
                .andExpect(jsonPath("$.data.status").value("simulated"));
    }

    @Test
    void shouldResolveRuleTargetWithOrLogic() throws Exception {
        String tenantCode = "send-" + UUID.randomUUID().toString().substring(0, 8);
        String authToken = createTenantAndLogin(mockMvc, tenantCode, "发送测试");

        Long accountId = createChannelAccount(authToken, tenantCode);
        Long personId = createPerson(authToken, tenantCode, 3);
        createPersonIdentity(authToken, tenantCode, personId, "wecom", "user-002");
        Long conversationId = createConversation(authToken, tenantCode, "wecom", "room-or-001");
        bindConversationAccount(authToken, tenantCode, conversationId, accountId);

        String sendPayload = "{\"targetType\":\"rule\",\"targetValue\":{\"rules\":[{\"field\":\"person_type\",\"operator\":\"eq\",\"value\":3},{\"field\":\"channel_type\",\"operator\":\"eq\",\"value\":\"wecom\"}],\"logic\":\"or\"},\"contentType\":\"text\",\"content\":{\"text\":\"hello\"},\"channelStrategy\":{\"channel_type\":\"wecom\",\"strategy\":\"primary\"}}";
        mockMvc.perform(post("/msg-platform/api/v1/send/test")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(sendPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data.totalCount").value(2))
                .andExpect(jsonPath("$.data.status").value("simulated"));
    }

    private Long createChannelAccount(String authToken, String tenantCode) throws Exception {
        String accountPayload = "{\"accountType\":1,\"channelType\":\"wecom\",\"accountId\":\"ZhangSan\",\"accountName\":\"张三\"}";
        String accountResponse = mockMvc.perform(post("/msg-platform/api/v1/channel-account")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(accountPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andReturn().getResponse().getContentAsString();
        return ((Number) com.jayway.jsonpath.JsonPath.read(accountResponse, "$.data.id")).longValue();
    }

    private Long createTag(String authToken, String tenantCode, String tagName) throws Exception {
        String tagPayload = String.format("{\"parentId\":0,\"tagName\":\"%s\"}", tagName);
        String tagResponse = mockMvc.perform(post("/msg-platform/api/v1/tag")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(tagPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andReturn().getResponse().getContentAsString();
        return ((Number) com.jayway.jsonpath.JsonPath.read(tagResponse, "$.data.id")).longValue();
    }

    private Long createPerson(String authToken, String tenantCode, int personType) throws Exception {
        String personPayload = String.format("{\"personCode\":\"P-%d\",\"personName\":\"张三\",\"phone\":\"13800138000\",\"personType\":%d}", personType, personType);
        String personResponse = mockMvc.perform(post("/msg-platform/api/v1/person")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(personPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andReturn().getResponse().getContentAsString();
        return ((Number) com.jayway.jsonpath.JsonPath.read(personResponse, "$.data.id")).longValue();
    }

    private void createPersonIdentity(String authToken, String tenantCode, Long personId,
                                      String channelType, String channelUserId) throws Exception {
        String identityPayload = String.format("{\"channelType\":\"%s\",\"channelUserId\":\"%s\"}", channelType, channelUserId);
        mockMvc.perform(post("/msg-platform/api/v1/person/" + personId + "/identities")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(identityPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0));
    }

    private void bindPersonTag(String authToken, String tenantCode, Long personId, Long tagId) throws Exception {
        String bindPayload = String.format("{\"tagIds\":[%d]}", tagId);
        mockMvc.perform(post("/msg-platform/api/v1/person/" + personId + "/bind-tags")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(bindPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0));
    }

    private Long createConversation(String authToken, String tenantCode, String channelType,
                                    String channelConversationId) throws Exception {
        String conversationPayload = String.format(
                "{\"conversationType\":2,\"channelType\":\"%s\",\"channelConversationId\":\"%s\",\"conversationName\":\"测试群\"}",
                channelType, channelConversationId);
        String conversationResponse = mockMvc.perform(post("/msg-platform/api/v1/conversation")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(conversationPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andReturn().getResponse().getContentAsString();
        return ((Number) com.jayway.jsonpath.JsonPath.read(conversationResponse, "$.data.id")).longValue();
    }

    private void bindConversationTag(String authToken, String tenantCode, Long conversationId,
                                     Long tagId) throws Exception {
        String bindPayload = String.format("{\"tagIds\":[%d]}", tagId);
        mockMvc.perform(post("/msg-platform/api/v1/conversation/" + conversationId + "/bind-tags")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(bindPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0));
    }

    private void bindConversationAccount(String authToken, String tenantCode, Long conversationId,
                                         Long accountId) throws Exception {
        String bindPayload = String.format("{\"accountId\":%d,\"bindingType\":1,\"sortOrder\":1}", accountId);
        mockMvc.perform(post("/msg-platform/api/v1/conversation/" + conversationId + "/bind-account")
                        .contextPath("/msg-platform")
                        .header("Authorization", authToken)
                        .header("X-Tenant-Code", tenantCode)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(bindPayload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0));
    }
}
