package com.sino.mci.channel.service;

import com.sino.mci.channel.dto.BindConversationAccountRequest;
import com.sino.mci.channel.entity.ChannelAccount;
import com.sino.mci.channel.entity.ChannelAccountConversation;
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.ConversationMapper;
import com.sino.mci.channel.repository.ConversationTagBindingMapper;
import com.sino.mci.crm.repository.TagMapper;
import com.sino.mci.exception.BusinessException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class ConversationServiceTest {

    @Mock
    private ConversationMapper conversationMapper;
    @Mock
    private ChannelAccountConversationMapper bindingMapper;
    @Mock
    private ConversationTagBindingMapper conversationTagBindingMapper;
    @Mock
    private TagMapper tagMapper;
    @Mock
    private ChannelAccountMapper accountMapper;

    @InjectMocks
    private ConversationService conversationService;

    @Test
    void bindAccount_rejectsAccountFromDifferentTenant() {
        Conversation conversation = createConversation();
        ChannelAccount account = createAccount(10L, "wecom");
        account.setTenantCode("other-tenant");

        when(conversationMapper.selectById(1L)).thenReturn(conversation);
        when(accountMapper.selectById(10L)).thenReturn(account);

        BindConversationAccountRequest request = createBindRequest(10L);

        BusinessException ex = assertThrows(BusinessException.class,
                () -> conversationService.bindAccount("tenant", 1L, request));
        assertEquals("账号不存在或不在当前租户", ex.getMessage());
    }

    @Test
    void bindAccount_rejectsAccountWithMismatchedChannelType() {
        Conversation conversation = createConversation();
        ChannelAccount account = createAccount(10L, "wechat_personal");

        when(conversationMapper.selectById(1L)).thenReturn(conversation);
        when(accountMapper.selectById(10L)).thenReturn(account);

        BindConversationAccountRequest request = createBindRequest(10L);

        BusinessException ex = assertThrows(BusinessException.class,
                () -> conversationService.bindAccount("tenant", 1L, request));
        assertEquals("账号渠道类型与会话不一致", ex.getMessage());
    }

    @Test
    void bindAccount_succeedsWhenAccountIsValid() {
        Conversation conversation = createConversation();
        ChannelAccount account = createAccount(10L, "wecom");

        when(conversationMapper.selectById(1L)).thenReturn(conversation);
        when(accountMapper.selectById(10L)).thenReturn(account);

        BindConversationAccountRequest request = createBindRequest(10L);
        conversationService.bindAccount("tenant", 1L, request);

        ArgumentCaptor<ChannelAccountConversation> captor = ArgumentCaptor.forClass(ChannelAccountConversation.class);
        verify(bindingMapper).insert(captor.capture());
        ChannelAccountConversation binding = captor.getValue();
        assertEquals(10L, binding.getAccountId());
        assertEquals(1L, binding.getConversationId());
        assertEquals(1, binding.getBindingType());
        assertEquals(0, binding.getSortOrder());
        assertEquals(1, binding.getStatus());
    }

    @Test
    void bindAccount_defaultsSortOrderToZeroWhenNull() {
        Conversation conversation = createConversation();
        ChannelAccount account = createAccount(10L, "wecom");

        when(conversationMapper.selectById(1L)).thenReturn(conversation);
        when(accountMapper.selectById(10L)).thenReturn(account);

        BindConversationAccountRequest request = createBindRequest(10L);
        request.setSortOrder(null);
        conversationService.bindAccount("tenant", 1L, request);

        ArgumentCaptor<ChannelAccountConversation> captor = ArgumentCaptor.forClass(ChannelAccountConversation.class);
        verify(bindingMapper).insert(captor.capture());
        assertEquals(0, captor.getValue().getSortOrder());
    }

    @Test
    void bindAccount_rejectsConversationFromDifferentTenant() {
        Conversation conversation = createConversation();
        conversation.setTenantCode("other-tenant");

        when(conversationMapper.selectById(1L)).thenReturn(conversation);

        BindConversationAccountRequest request = createBindRequest(10L);

        BusinessException ex = assertThrows(BusinessException.class,
                () -> conversationService.bindAccount("tenant", 1L, request));
        assertEquals("会话不存在", ex.getMessage());
    }

    @Test
    void bindAccount_rejectsConversationNotFound() {
        when(conversationMapper.selectById(1L)).thenReturn(null);

        BindConversationAccountRequest request = createBindRequest(10L);

        BusinessException ex = assertThrows(BusinessException.class,
                () -> conversationService.bindAccount("tenant", 1L, request));
        assertEquals("会话不存在", ex.getMessage());
    }

    private Conversation createConversation() {
        Conversation conversation = new Conversation();
        conversation.setId(1L);
        conversation.setTenantCode("tenant");
        conversation.setChannelType("wecom");
        conversation.setStatus(1);
        return conversation;
    }

    private ChannelAccount createAccount(Long id, String channelType) {
        ChannelAccount account = new ChannelAccount();
        account.setId(id);
        account.setTenantCode("tenant");
        account.setChannelType(channelType);
        account.setStatus(1);
        return account;
    }

    private BindConversationAccountRequest createBindRequest(Long accountId) {
        BindConversationAccountRequest request = new BindConversationAccountRequest();
        request.setAccountId(accountId);
        request.setBindingType(1);
        request.setSortOrder(0);
        return request;
    }
}
