package com.red.circle.other.app.listener.user;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.mq.MessageEventProcess;
import com.red.circle.component.mq.MessageEventProcessDescribe;
import com.red.circle.component.mq.service.Action;
import com.red.circle.component.mq.service.ConsumerMessage;
import com.red.circle.external.inner.endpoint.message.ImMessageClient;
import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.mq.business.model.event.user.CpApplyEvent;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.enums.user.user.CpApplyStatus;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;

class CpApplyListenerTest {

  @Test
  void waitApplyExpiredShouldSendOneC2cMessageToConversation() {
    CpApplyService cpApplyService = mock(CpApplyService.class);
    ImMessageClient imMessageClient = mock(ImMessageClient.class);
    UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
    MessageEventProcess messageEventProcess = mock(MessageEventProcess.class);
    CpApply apply = new CpApply()
        .setId(3001L)
        .setSendApplyUserId(1001L)
        .setAcceptApplyUserId(2001L)
        .setRelationType("SISTERS")
        .setStatus(CpApplyStatus.WAIT.name());

    when(cpApplyService.getById(3001L)).thenReturn(apply);
    when(cpApplyService.expireWaitById(3001L)).thenReturn(true);
    when(userProfileGateway.getByUserId(1001L)).thenReturn(profile(1001L));
    when(userProfileGateway.getByUserId(2001L)).thenReturn(profile(2001L));
    when(imMessageClient.sendCustomMessage(any(CustomC2cMsgBodyCmd.class)))
        .thenReturn(ResultResponse.success());
    when(messageEventProcess.consume(any(MessageEventProcessDescribe.class),
        eq(CpApplyEvent.class), any())).thenAnswer(invocation -> {
      @SuppressWarnings("unchecked")
      Consumer<CpApplyEvent> consumer = invocation.getArgument(2, Consumer.class);
      consumer.accept(new CpApplyEvent()
          .setId(3001L)
          .setSysOrigin(SysOriginPlatformEnum.LIKEI));
      return Action.SUCCESS;
    });

    CpApplyListener listener = new CpApplyListener(cpApplyService, imMessageClient,
        userProfileGateway, messageEventProcess);

    assertEquals(Action.SUCCESS, listener.consume(new ConsumerMessage()));

    ArgumentCaptor<CustomC2cMsgBodyCmd> captor = ArgumentCaptor.forClass(
        CustomC2cMsgBodyCmd.class);
    InOrder inOrder = inOrder(cpApplyService, imMessageClient);
    inOrder.verify(cpApplyService).expireWaitById(3001L);
    inOrder.verify(imMessageClient).sendCustomMessage(captor.capture());
    List<CustomC2cMsgBodyCmd> messages = captor.getAllValues();
    assertExpiredMessage(messages.get(0), "1001", "2001");
  }

  @Test
  void nonWaitApplyShouldNotExpireAgain() {
    CpApplyService cpApplyService = mock(CpApplyService.class);
    ImMessageClient imMessageClient = mock(ImMessageClient.class);
    UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
    MessageEventProcess messageEventProcess = mock(MessageEventProcess.class);
    CpApply apply = new CpApply()
        .setId(3001L)
        .setStatus(CpApplyStatus.EXPIRE.name());

    when(cpApplyService.getById(3001L)).thenReturn(apply);
    when(messageEventProcess.consume(any(MessageEventProcessDescribe.class),
        eq(CpApplyEvent.class), any())).thenAnswer(invocation -> {
      @SuppressWarnings("unchecked")
      Consumer<CpApplyEvent> consumer = invocation.getArgument(2, Consumer.class);
      consumer.accept(new CpApplyEvent()
          .setId(3001L)
          .setSysOrigin(SysOriginPlatformEnum.LIKEI));
      return Action.SUCCESS;
    });

    CpApplyListener listener = new CpApplyListener(cpApplyService, imMessageClient,
        userProfileGateway, messageEventProcess);

    assertEquals(Action.SUCCESS, listener.consume(new ConsumerMessage()));
    verify(cpApplyService, never()).expireWaitById(3001L);
    verify(imMessageClient, never()).sendCustomMessage(any(CustomC2cMsgBodyCmd.class));
  }

  @SuppressWarnings("unchecked")
  private static void assertExpiredMessage(CustomC2cMsgBodyCmd message, String fromAccount,
      String toAccount) {
    assertEquals(fromAccount, message.getFromAccount());
    assertEquals(toAccount, message.getToAccount());
    assertEquals("CP_BUILD", message.getDesc());
    Map<String, Object> data = (Map<String, Object>) message.getData();
    assertEquals("RELATION_EXPIRED", data.get("noticeAction"));
    assertEquals("RELATION_EXPIRED", data.get("action"));
    assertEquals("EXPIRED", data.get("status"));
    assertEquals("EXPIRED", data.get("result"));
    assertEquals("EXPIRED", data.get("state"));
    assertEquals(Boolean.TRUE, data.get("expired"));
    assertEquals("SISTERS", data.get("relationType"));
    assertTrue(data.get("content").toString().contains("\"relationType\":\"SISTERS\""));
  }

  private static UserProfile profile(Long userId) {
    UserProfile userProfile = new UserProfile();
    userProfile.setId(userId);
    userProfile.setAccount(userId.toString());
    userProfile.setUserNickname("user-" + userId);
    userProfile.setCountryCode("SA");
    userProfile.setCountryName("Saudi Arabia");
    return userProfile;
  }
}
