package com.sino.mci.channel.wecom;

import com.sino.mci.channel.common.dto.ChannelInitRequest;
import com.sino.mci.channel.common.dto.ChannelInitResult;
import com.sino.mci.channel.common.dto.SendMessageRequest;
import com.sino.mci.channel.common.dto.SendMessageResult;
import com.sino.mci.channel.wecom.support.WeComServiceFactory;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpChatService;
import me.chanjar.weixin.cp.api.WxCpMessageService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpAppChatMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

import java.util.HashMap;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class WeComChannelAdapterTest {

    private static Map<String, Object> validConfig() {
        Map<String, Object> config = new HashMap<>();
        config.put("corp_id", "ww123");
        config.put("agent_id", "1000002");
        config.put("secret", "secret-abc");
        return config;
    }

    private static SendMessageRequest textMessageRequest() {
        SendMessageRequest request = new SendMessageRequest();
        request.setReceiverId("user1,user2");
        request.setContentType("text");
        Map<String, Object> content = new HashMap<>();
        content.put("text", "hello");
        request.setContent(content);
        request.setExtra(validConfig());
        return request;
    }

    @Test
    void shouldConstructAndSendTextMessage() throws WxErrorException {
        WxCpMessageService messageService = mock(WxCpMessageService.class);
        WxCpChatService chatService = mock(WxCpChatService.class);
        WxCpService service = mock(WxCpService.class);
        when(service.getMessageService()).thenReturn(messageService);
        when(service.getChatService()).thenReturn(chatService);
        when(messageService.send(any(WxCpMessage.class))).thenReturn(new WxCpMessageSendResult());

        WeComServiceFactory factory = mock(WeComServiceFactory.class);
        when(factory.getOrCreateService(any(Map.class))).thenReturn(service);

        WeComChannelAdapter adapter = new WeComChannelAdapter(factory);
        SendMessageResult result = adapter.sendMessage(textMessageRequest());

        assertThat(result.isSuccess()).isTrue();
        verify(service, never()).getChatService();

        ArgumentCaptor<WxCpMessage> captor = ArgumentCaptor.forClass(WxCpMessage.class);
        verify(messageService).send(captor.capture());
        WxCpMessage message = captor.getValue();
        assertThat(message.getMsgType()).isEqualTo("text");
        assertThat(message.getContent()).isEqualTo("hello");
        assertThat(message.getToUser()).isEqualTo("user1|user2");
        assertThat(message.getAgentId()).isEqualTo(1000002);
    }

    @Test
    void shouldReturnFailureWhenConfigInvalid() throws WxErrorException {
        WxCpService service = mock(WxCpService.class);
        when(service.getAccessToken(false)).thenThrow(new WxErrorException(WxError.builder().errorMsg("invalid credential").build()));

        WeComServiceFactory factory = mock(WeComServiceFactory.class);
        when(factory.getOrCreateService(any(Map.class))).thenReturn(service);

        WeComChannelAdapter adapter = new WeComChannelAdapter(factory);

        ChannelInitRequest request = new ChannelInitRequest();
        request.setConfig(validConfig());

        ChannelInitResult result = adapter.initAccount(request);

        assertThat(result.isSuccess()).isFalse();
        assertThat(result.getMessage()).contains("invalid credential");
    }

    @Test
    void shouldReturnFailureWhenConfigMissing() {
        WeComServiceFactory factory = mock(WeComServiceFactory.class);
        when(factory.getOrCreateService(any(Map.class))).thenReturn(null);

        WeComChannelAdapter adapter = new WeComChannelAdapter(factory);

        ChannelInitRequest request = new ChannelInitRequest();
        request.setConfig(new HashMap<>());

        ChannelInitResult result = adapter.initAccount(request);

        assertThat(result.isSuccess()).isFalse();
        assertThat(result.getMessage()).contains("corp_id/agent_id/secret");
    }

    @Test
    void shouldSendGroupChatMessageWhenConversationIdPresent() throws WxErrorException {
        WxCpMessageService messageService = mock(WxCpMessageService.class);
        WxCpChatService chatService = mock(WxCpChatService.class);
        WxCpService service = mock(WxCpService.class);
        when(service.getMessageService()).thenReturn(messageService);
        when(service.getChatService()).thenReturn(chatService);

        WeComServiceFactory factory = mock(WeComServiceFactory.class);
        when(factory.getOrCreateService(any(Map.class))).thenReturn(service);

        WeComChannelAdapter adapter = new WeComChannelAdapter(factory);

        SendMessageRequest request = textMessageRequest();
        request.setConversationId("chat123");

        SendMessageResult result = adapter.sendMessage(request);

        assertThat(result.isSuccess()).isTrue();
        verify(service, never()).getMessageService();

        ArgumentCaptor<WxCpAppChatMessage> captor = ArgumentCaptor.forClass(WxCpAppChatMessage.class);
        verify(chatService).sendMsg(captor.capture());
        WxCpAppChatMessage message = captor.getValue();
        assertThat(message.getMsgType()).isEqualTo("text");
        assertThat(message.getChatId()).isEqualTo("chat123");
        assertThat(message.getContent()).isEqualTo("hello");
    }
}
