package com.sino.mci.send.service;

import com.sino.mci.channel.common.model.AccountType;
import com.sino.mci.channel.common.model.ChannelType;
import com.sino.mci.channel.entity.ChannelAccount;
import com.sino.mci.channel.entity.ChannelAccountConversation;
import com.sino.mci.channel.entity.ChannelSubject;
import com.sino.mci.channel.entity.Conversation;
import com.sino.mci.channel.repository.ChannelAccountConversationMapper;
import com.sino.mci.channel.repository.ChannelAccountMapper;
import com.sino.mci.channel.repository.ChannelSubjectMapper;
import com.sino.mci.channel.repository.ConversationMapper;
import com.sino.mci.crm.entity.ChannelIdentity;
import com.sino.mci.crm.entity.Person;
import com.sino.mci.crm.repository.ChannelIdentityMapper;
import com.sino.mci.crm.repository.PersonMapper;
import com.sino.mci.send.entity.SendRecord;
import com.sino.mci.send.repository.SendRecordMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

import static com.sino.mci.test.AuthTestHelper.createTenantAndLogin;
import static org.assertj.core.api.Assertions.assertThat;
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>覆盖自动路由策略：
 * <ul>
 *   <li>内部群（conversation_scene = 1）→ WECOM / WECOM_BOT</li>
 *   <li>外部群（conversation_scene = 2）→ WECHAT_PERSONAL</li>
 *   <li>内部人员 → WECOM_AGENT</li>
 *   <li>外部人员 → WECOM_AGENT</li>
 * </ul>
 */
@SpringBootTest
@AutoConfigureMockMvc
@Transactional
@TestPropertySource(properties = {
        "spring.rabbitmq.listener.simple.auto-startup=false"
})
class SendRoutingIntegrationTest {

    private static final String CHANNEL_TYPE_WECOM = ChannelType.WECOM.name().toLowerCase();
    private static final String CHANNEL_TYPE_WECHAT_PERSONAL = ChannelType.WECHAT_PERSONAL.name().toLowerCase();

    private static final int CONVERSATION_SCENE_INTERNAL = 1;
    private static final int CONVERSATION_SCENE_EXTERNAL = 2;

    private static final int PERSON_TYPE_INTERNAL = 1;
    private static final int PERSON_TYPE_EXTERNAL = 3;

    private static final int STATUS_ENABLED = 1;
    private static final int BINDING_TYPE_SEND_PRIMARY = 1;

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ChannelSubjectMapper subjectMapper;

    @Autowired
    private ChannelAccountMapper accountMapper;

    @Autowired
    private ConversationMapper conversationMapper;

    @Autowired
    private ChannelAccountConversationMapper accountConversationMapper;

    @Autowired
    private PersonMapper personMapper;

    @Autowired
    private ChannelIdentityMapper channelIdentityMapper;

    @Autowired
    private SendRecordMapper sendRecordMapper;

    @Test
    void shouldRouteInternalGroupToWeComBot() throws Exception {
        String tenantCode = "routing-int-group-" + UUID.randomUUID().toString().substring(0, 8);
        String authToken = createTenantAndLogin(mockMvc, tenantCode, "内部群路由测试");

        ChannelSubject subject = createWeComSubject(tenantCode);
        ChannelAccount botAccount = createWeComBotAccount(tenantCode, subject.getId());
        Conversation conversation = createConversation(tenantCode, subject.getId(),
                CONVERSATION_SCENE_INTERNAL, "内部群");
        bindAccountToConversation(botAccount.getId(), conversation.getId());

        String taskId = sendTestMessage(authToken, tenantCode,
                buildConversationPayload(conversation.getId()));

        List<SendRecord> records = sendRecordMapper.selectList(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<SendRecord>()
                        .eq(SendRecord::getTenantCode, tenantCode)
                        .eq(SendRecord::getTaskId, taskId));

        assertThat(records).hasSize(1);
        SendRecord record = records.get(0);
        assertThat(record.getChannelType()).isEqualTo(CHANNEL_TYPE_WECOM);
        assertThat(record.getAccountId()).isEqualTo(botAccount.getId());
        assertThat(record.getConversationId()).isEqualTo(conversation.getId());
    }

    @Test
    void shouldRouteExternalGroupToWeChatPersonal() throws Exception {
        String tenantCode = "routing-ext-group-" + UUID.randomUUID().toString().substring(0, 8);
        String authToken = createTenantAndLogin(mockMvc, tenantCode, "外部群路由测试");

        ChannelSubject subject = createWeComSubject(tenantCode);
        ChannelAccount pywechatAccount = createWeChatPersonalAccount(tenantCode, subject.getId());
        Conversation conversation = createConversation(tenantCode, subject.getId(),
                CONVERSATION_SCENE_EXTERNAL, "外部群");
        bindAccountToConversation(pywechatAccount.getId(), conversation.getId());

        String taskId = sendTestMessage(authToken, tenantCode,
                buildConversationPayload(conversation.getId()));

        List<SendRecord> records = sendRecordMapper.selectList(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<SendRecord>()
                        .eq(SendRecord::getTenantCode, tenantCode)
                        .eq(SendRecord::getTaskId, taskId));

        assertThat(records).hasSize(1);
        SendRecord record = records.get(0);
        assertThat(record.getChannelType()).isEqualTo(CHANNEL_TYPE_WECHAT_PERSONAL);
        assertThat(record.getAccountId()).isEqualTo(pywechatAccount.getId());
        assertThat(record.getConversationId()).isEqualTo(conversation.getId());
    }

    @Test
    void shouldRouteInternalPersonToWeComAgent() throws Exception {
        String tenantCode = "routing-int-person-" + UUID.randomUUID().toString().substring(0, 8);
        String authToken = createTenantAndLogin(mockMvc, tenantCode, "内部人员路由测试");

        ChannelSubject subject = createWeComSubject(tenantCode);
        ChannelAccount agentAccount = createWeComAgentAccount(tenantCode, subject.getId());
        Person person = createPerson(tenantCode, "int-user", "内部人员", PERSON_TYPE_INTERNAL);
        createChannelIdentity(tenantCode, person.getId(), subject.getId(), "int-user");

        String taskId = sendTestMessage(authToken, tenantCode,
                buildPersonPayload(person.getId()));

        List<SendRecord> records = sendRecordMapper.selectList(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<SendRecord>()
                        .eq(SendRecord::getTenantCode, tenantCode)
                        .eq(SendRecord::getTaskId, taskId));

        assertThat(records).hasSize(1);
        SendRecord record = records.get(0);
        assertThat(record.getChannelType()).isEqualTo(CHANNEL_TYPE_WECOM);
        assertThat(record.getAccountId()).isEqualTo(agentAccount.getId());
        assertThat(record.getPersonId()).isEqualTo(person.getId());
    }

    @Test
    void shouldRouteExternalPersonToWeComAgent() throws Exception {
        String tenantCode = "routing-ext-person-" + UUID.randomUUID().toString().substring(0, 8);
        String authToken = createTenantAndLogin(mockMvc, tenantCode, "外部人员路由测试");

        ChannelSubject subject = createWeComSubject(tenantCode);
        ChannelAccount agentAccount = createWeComAgentAccount(tenantCode, subject.getId());
        Person person = createPerson(tenantCode, "ext-user", "外部人员", PERSON_TYPE_EXTERNAL);
        createChannelIdentity(tenantCode, person.getId(), subject.getId(), "ext-user");

        String taskId = sendTestMessage(authToken, tenantCode,
                buildPersonPayload(person.getId()));

        List<SendRecord> records = sendRecordMapper.selectList(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<SendRecord>()
                        .eq(SendRecord::getTenantCode, tenantCode)
                        .eq(SendRecord::getTaskId, taskId));

        assertThat(records).hasSize(1);
        SendRecord record = records.get(0);
        assertThat(record.getChannelType()).isEqualTo(CHANNEL_TYPE_WECOM);
        assertThat(record.getAccountId()).isEqualTo(agentAccount.getId());
        assertThat(record.getPersonId()).isEqualTo(person.getId());
    }

    private String sendTestMessage(String authToken, String tenantCode, String payload) throws Exception {
        String response = 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(payload))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data.totalCount").value(1))
                .andExpect(jsonPath("$.data.status").value("simulated"))
                .andReturn().getResponse().getContentAsString();
        return com.jayway.jsonpath.JsonPath.read(response, "$.data.taskId");
    }

    private String buildConversationPayload(Long conversationId) {
        return String.format(
                "{\"targetType\":\"conversation\",\"targetValue\":{\"conversation_id\":%d},\"contentType\":\"text\",\"content\":{\"text\":\"hello\"},\"channelStrategy\":{\"channel_type\":\"wecom\",\"strategy\":\"primary\"}}",
                conversationId);
    }

    private String buildPersonPayload(Long personId) {
        return String.format(
                "{\"targetType\":\"person\",\"targetValue\":{\"person_id\":%d},\"contentType\":\"text\",\"content\":{\"text\":\"hello\"},\"channelStrategy\":{\"channel_type\":\"wecom\"}}",
                personId);
    }

    private ChannelSubject createWeComSubject(String tenantCode) {
        ChannelSubject subject = new ChannelSubject();
        subject.setTenantCode(tenantCode);
        subject.setChannelType(CHANNEL_TYPE_WECOM);
        subject.setSubjectCode("sub-" + UUID.randomUUID().toString().substring(0, 8));
        subject.setSubjectName("测试主体");
        subject.setCorpId("corp-" + UUID.randomUUID().toString().substring(0, 8));
        subject.setAgentId("1000002");
        subject.setAgentSecret("secret-" + UUID.randomUUID());
        subject.setStatus(STATUS_ENABLED);
        subjectMapper.insert(subject);
        return subject;
    }

    private ChannelAccount createWeComBotAccount(String tenantCode, Long subjectId) {
        ChannelAccount account = new ChannelAccount();
        account.setTenantCode(tenantCode);
        account.setAccountType(AccountType.WECOM_BOT.getCode());
        account.setChannelType(CHANNEL_TYPE_WECOM);
        account.setSubjectId(subjectId);
        account.setAccountId("bot-" + UUID.randomUUID().toString().substring(0, 8));
        account.setAccountName("企微群机器人");
        account.setStatus(STATUS_ENABLED);
        accountMapper.insert(account);
        return account;
    }

    private ChannelAccount createWeComAgentAccount(String tenantCode, Long subjectId) {
        ChannelAccount account = new ChannelAccount();
        account.setTenantCode(tenantCode);
        account.setAccountType(AccountType.WECOM_AGENT.getCode());
        account.setChannelType(CHANNEL_TYPE_WECOM);
        account.setSubjectId(subjectId);
        account.setAccountId("agent-" + UUID.randomUUID().toString().substring(0, 8));
        account.setAccountName("企微应用");
        account.setStatus(STATUS_ENABLED);
        accountMapper.insert(account);
        return account;
    }

    private ChannelAccount createWeChatPersonalAccount(String tenantCode, Long subjectId) {
        ChannelAccount account = new ChannelAccount();
        account.setTenantCode(tenantCode);
        account.setAccountType(AccountType.WECHAT_PERSONAL.getCode());
        account.setChannelType(CHANNEL_TYPE_WECHAT_PERSONAL);
        account.setSubjectId(subjectId);
        account.setAccountId("wxid-" + UUID.randomUUID().toString().substring(0, 8));
        account.setAccountName("pywechat 个人号");
        account.setStatus(STATUS_ENABLED);
        account.setOnlineStatus(STATUS_ENABLED);
        account.setRiskLevel(1);
        account.setLastHeartbeat(LocalDateTime.now());
        accountMapper.insert(account);
        return account;
    }

    private Conversation createConversation(String tenantCode, Long subjectId,
                                            Integer conversationScene, String conversationName) {
        Conversation conversation = new Conversation();
        conversation.setTenantCode(tenantCode);
        conversation.setConversationType(2);
        conversation.setChannelType(CHANNEL_TYPE_WECOM);
        conversation.setChannelConversationId("chat-" + UUID.randomUUID().toString().substring(0, 8));
        conversation.setSubjectId(subjectId);
        conversation.setConversationName(conversationName);
        conversation.setConversationScene(conversationScene);
        conversation.setCreateSource(1);
        conversation.setStatus(STATUS_ENABLED);
        conversationMapper.insert(conversation);
        return conversation;
    }

    private void bindAccountToConversation(Long accountId, Long conversationId) {
        ChannelAccountConversation binding = new ChannelAccountConversation();
        binding.setAccountId(accountId);
        binding.setConversationId(conversationId);
        binding.setBindingType(BINDING_TYPE_SEND_PRIMARY);
        binding.setSortOrder(1);
        binding.setStatus(STATUS_ENABLED);
        accountConversationMapper.insert(binding);
    }

    private Person createPerson(String tenantCode, String personCode, String personName, Integer personType) {
        Person person = new Person();
        person.setTenantCode(tenantCode);
        person.setPersonCode(personCode + "-" + UUID.randomUUID().toString().substring(0, 8));
        person.setPersonName(personName);
        person.setPhone("13800138000");
        person.setPersonType(personType);
        person.setStatus(STATUS_ENABLED);
        personMapper.insert(person);
        return person;
    }

    private void createChannelIdentity(String tenantCode, Long personId, Long subjectId, String channelUserId) {
        ChannelIdentity identity = new ChannelIdentity();
        identity.setTenantCode(tenantCode);
        identity.setPersonId(personId);
        identity.setChannelType(CHANNEL_TYPE_WECOM);
        identity.setChannelUserId(channelUserId + "-" + UUID.randomUUID().toString().substring(0, 8));
        identity.setChannelUserName("测试用户");
        identity.setSubjectId(subjectId);
        identity.setStatus(STATUS_ENABLED);
        channelIdentityMapper.insert(identity);
    }
}
