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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
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 static org.mockito.ArgumentMatchers.any;

import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpDismissApplyCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.common.user.UserGiftBackpackCommon;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.cache.service.cp.CpRelationshipCacheService;
import com.red.circle.other.infra.database.cache.service.user.UserCpValueCacheService;
import com.red.circle.other.infra.database.mongo.service.user.count.WeekCpValueCountService;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordService;
import com.red.circle.other.infra.database.rds.service.user.user.CpCabinAchieveService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipStatus;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
import java.math.BigDecimal;
import java.util.Arrays;
import org.junit.jupiter.api.Test;

class DismissCpApplyCmdExeTest {

  @Test
  void dismissShouldCleanRelationshipImmediatelyWhenCooldownIsZero() {
    Fixture fixture = new Fixture();
    when(fixture.cpRelationshipService.getByUserId(1001L, 2001L, "CP"))
        .thenReturn(relationship());

    CpApplyDismissCmd cmd = new CpApplyDismissCmd()
        .setCpUserId(2001L)
        .setRelationType("CP");
    cmd.setReqUserId(1001L);

    CpDismissApplyCO result = fixture.exe.execute(cmd);

    assertEquals("NONE", result.getStatus());
    assertEquals("CP", result.getRelationType());
    assertEquals(2001L, result.getCpUserId());
    assertEquals(0L, result.getDismissRemainSeconds());
    assertNotNull(result.getDismissEndTime());

    verify(fixture.cpApplyService).dismiss(1001L, "CP");
    verify(fixture.cpRelationshipService).updateToDismissing(1001L, 2001L, "CP");
    verify(fixture.cpRelationshipService).deleteByUserPair(1001L, 2001L, "CP");
    verify(fixture.weekCpValueCountService).removeThisWeek(1001L, 2001L);
    verify(fixture.weekCpValueCountService).removeThisSeason(1001L, 2001L);
    verify(fixture.cpValueService).deleteById(3001L);
    verify(fixture.userCpValueCacheService).remove(1001L, 2001L);
    verify(fixture.cpCabinAchieveService).deleteByCpValId(3001L);
    verify(fixture.cpBlessRecordService).deleteByCpValId(3001L);
    verify(fixture.cpRelationshipCacheService).remove(Arrays.asList(1001L, 2001L));
    verify(fixture.walletGoldClient).changeBalance(any(GoldReceiptCmd.class));
  }

  @Test
  void dismissShouldCleanExistingDismissingRelationship() {
    Fixture fixture = new Fixture();
    CpRelationship relationship = relationship().setStatus(CpRelationshipStatus.DISMISSING.name());
    when(fixture.cpRelationshipService.getDismissingCp(1001L, 2001L, "CP"))
        .thenReturn(relationship);

    CpApplyDismissCmd cmd = new CpApplyDismissCmd()
        .setCpUserId(2001L)
        .setRelationType("CP");
    cmd.setReqUserId(1001L);

    CpDismissApplyCO result = fixture.exe.execute(cmd);

    assertEquals("NONE", result.getStatus());
    assertEquals(0L, result.getDismissRemainSeconds());
    verify(fixture.cpRelationshipService).deleteByUserPair(1001L, 2001L, "CP");
    verify(fixture.walletGoldClient, never()).changeBalance(any(GoldReceiptCmd.class));
  }

  private static CpRelationship relationship() {
    return new CpRelationship()
        .setId(4001L)
        .setUserId(1001L)
        .setCpUserId(2001L)
        .setCpValId(3001L)
        .setSysOrigin("LIKEI")
        .setRelationType("CP")
        .setStatus(CpRelationshipStatus.NORMAL.name());
  }

  private static UserProfile userProfile() {
    UserProfile userProfile = new UserProfile();
    userProfile.setId(1001L);
    userProfile.setAccount("1001");
    userProfile.setUserNickname("user-1001");
    userProfile.setUserAvatar("avatar");
    userProfile.setCountryCode("SA");
    userProfile.setCountryName("Saudi Arabia");
    return userProfile;
  }

  private static class Fixture {

    private final CpValueService cpValueService = mock(CpValueService.class);
    private final CpApplyService cpApplyService = mock(CpApplyService.class);
    private final CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
    private final UserCpValueCacheService userCpValueCacheService = mock(
        UserCpValueCacheService.class);
    private final WeekCpValueCountService weekCpValueCountService = mock(
        WeekCpValueCountService.class);
    private final CpBlessRecordService cpBlessRecordService = mock(CpBlessRecordService.class);
    private final CpCabinAchieveService cpCabinAchieveService = mock(CpCabinAchieveService.class);
    private final CpRelationshipCacheService cpRelationshipCacheService = mock(
        CpRelationshipCacheService.class);
    private final WalletGoldClient walletGoldClient = mock(WalletGoldClient.class);
    private final EnumConfigCacheService enumConfigCacheService = mock(EnumConfigCacheService.class);
    private final DismissCpApplyCmdExe exe;

    private Fixture() {
      OfficialNoticeClient officialNoticeClient = mock(OfficialNoticeClient.class);
      UserGiftBackpackCommon userGiftBackpackCommon = mock(UserGiftBackpackCommon.class);
      UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
      when(userProfileGateway.getByUserId(1001L)).thenReturn(userProfile());
      when(enumConfigCacheService.getValueBigDecimal(
          EnumConfigKey.CP_DISMISS_CONSUME_GOLD, "LIKEI"))
          .thenReturn(BigDecimal.valueOf(100));
      when(walletGoldClient.changeBalance(any(GoldReceiptCmd.class)))
          .thenReturn(ResultResponse.success(new WalletReceiptResDTO()));

      exe = new DismissCpApplyCmdExe(
          cpValueService,
          cpApplyService,
          officialNoticeClient,
          cpRelationshipService,
          userGiftBackpackCommon,
          weekCpValueCountService,
          userCpValueCacheService,
          cpBlessRecordService,
          cpCabinAchieveService,
          userProfileGateway,
          cpRelationshipCacheService,
          walletGoldClient,
          enumConfigCacheService);
    }
  }
}
