package com.red.circle.other.app.inner.service.team.impl;

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.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
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.component.redis.service.RedisService;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillMemberTarget;
import com.red.circle.other.infra.database.mongo.dto.team.TeamPolicy;
import com.red.circle.other.infra.database.mongo.dto.team.TeamSalaryPaymentDetails;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamSalaryPaymentService;
import com.red.circle.other.inner.enums.team.TeamMemberRole;
import com.red.circle.other.inner.enums.team.TeamMemberTargetIndex;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.wallet.inner.endpoint.bank.UserBankBalanceClient;
import com.red.circle.wallet.inner.endpoint.bank.UserBankRunningWaterClient;
import com.red.circle.wallet.inner.model.dto.BankBalanceDTO;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.test.util.ReflectionTestUtils;

class TeamManualSalaryPaymentClientServiceImplTest {

  @Test
  void buildSettlementPaymentsShouldTopUpWhenCurrentLevelRecordExistsButSalaryIsInsufficient()
      throws Exception {
    TeamManualSalaryPaymentClientServiceImpl service =
        new TeamManualSalaryPaymentClientServiceImpl(
            null, null, null, null, null, null, null, null);
    Long userId = 2054591808909930498L;
    String region = "2046067717605224449";

    TeamBillMemberTarget billTarget = new TeamBillMemberTarget()
        .setLevel(6)
        .setMemberSalary(new BigDecimal("35.00"))
        .setOwnSalary(new BigDecimal("8.75"));
    TeamSalaryPayment currentPayment = new TeamSalaryPayment()
        .setSalarySendDetails(List.of(new TeamSalaryPaymentDetails()
            .setTeamMemberId(userId)
            .setTeamRegionId(region)
            .setPolicyLevel(6)
            .setAnchor(Boolean.TRUE)
            .setSalary(new BigDecimal("3.00"))
            .setRecycled(Boolean.FALSE)));

    Object paymentSummary = paymentSummary(
        new BigDecimal("31.50"),
        new BigDecimal("22.00"),
        new BigDecimal("9.50"));

    List<?> payments = ReflectionTestUtils.invokeMethod(service, "buildSettlementPayments",
        null, billTarget, List.of(currentPayment), userId, region, paymentSummary, false);

    assertEquals(2, payments.size());
    Object payment = payments.get(0);
    assertEquals(6, ReflectionTestUtils.getField(payment, "level"));
    assertEquals(Boolean.TRUE, ReflectionTestUtils.getField(payment, "anchor"));
    assertEquals(new BigDecimal("13.00"), ReflectionTestUtils.getField(payment, "salary"));
    assertEquals(Boolean.FALSE, ReflectionTestUtils.getField(payments.get(1), "anchor"));
    assertEquals(BigDecimal.ZERO.setScale(2), ReflectionTestUtils.getField(payments.get(1),
        "salary"));
  }

  @Test
  void buildSettlementPaymentsShouldSettleZeroWhenPayableSalaryIsNegative() throws Exception {
    TeamManualSalaryPaymentClientServiceImpl service =
        new TeamManualSalaryPaymentClientServiceImpl(
            null, null, null, null, null, null, null, null);
    Long userId = 1008071L;
    String region = "2046067717605224449";

    TeamBillMemberTarget billTarget = new TeamBillMemberTarget()
        .setLevel(6)
        .setMemberSalary(new BigDecimal("35.00"))
        .setOwnSalary(new BigDecimal("8.75"));
    Object paymentSummary = paymentSummary(
        new BigDecimal("45.00"),
        new BigDecimal("36.00"),
        new BigDecimal("9.00"));

    List<?> payments = ReflectionTestUtils.invokeMethod(service, "buildSettlementPayments",
        null, billTarget, List.of(), userId, region, paymentSummary, false);

    assertEquals(2, payments.size());
    assertEquals(BigDecimal.ZERO.setScale(2), ReflectionTestUtils.getField(payments.get(0),
        "salary"));
    assertEquals(BigDecimal.ZERO.setScale(2), ReflectionTestUtils.getField(payments.get(1),
        "salary"));
  }

  @Test
  void resolveBillBelongShouldRejectInvalidMonth() {
    TeamManualSalaryPaymentClientServiceImpl service =
        new TeamManualSalaryPaymentClientServiceImpl(
            null, null, null, null, null, null, null, null);

    assertThrows(RuntimeException.class,
        () -> ReflectionTestUtils.invokeMethod(service, "resolveBillBelong", 202613));
  }

  @Test
  void buildRowsShouldIgnoreHistoryTargetsBeforePolicyCalculation() {
    RedisService redisService = mock(RedisService.class);
    TeamMemberService teamMemberService = mock(TeamMemberService.class);
    TeamProfileService teamProfileService = mock(TeamProfileService.class);
    UserBankBalanceClient userBankBalanceClient = mock(UserBankBalanceClient.class);
    TeamMemberTargetService teamMemberTargetService = mock(TeamMemberTargetService.class);
    TeamSalaryPaymentService teamSalaryPaymentService = mock(TeamSalaryPaymentService.class);
    TeamPolicyManagerService teamPolicyManagerService = mock(TeamPolicyManagerService.class);
    UserBankRunningWaterClient userBankRunningWaterClient = mock(UserBankRunningWaterClient.class);
    TeamManualSalaryPaymentClientServiceImpl service = new TeamManualSalaryPaymentClientServiceImpl(
        redisService, teamMemberService, teamProfileService, userBankBalanceClient,
        teamMemberTargetService, teamSalaryPaymentService, teamPolicyManagerService,
        userBankRunningWaterClient);

    String sysOrigin = "LIKEI";
    String countryCode = "BD";
    String region = "2045389832842178561";
    Long userId = 2050266954857508866L;
    Long teamId = 2046582184822763522L;
    Integer billBelong = 202606;
    TeamMemberTarget historyTarget = new TeamMemberTarget()
        .setId("history-target")
        .setTimeId(2061195378987954177L)
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setTeamId(teamId)
        .setUserId(userId)
        .setBillBelong(billBelong)
        .setHistory(Boolean.TRUE)
        .setDailyTargets(List.of(new TeamMemberTargetIndex()
            .setOwnOnlineTime(21L)
            .setAcceptGiftValue(662505L)));
    TeamMemberTarget activeTarget = new TeamMemberTarget()
        .setId("active-target")
        .setTimeId(2062879908861898754L)
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setTeamId(teamId)
        .setUserId(userId)
        .setBillBelong(billBelong)
        .setHistory(Boolean.FALSE)
        .setDailyTargets(List.of(new TeamMemberTargetIndex()
            .setOwnOnlineTime(38L)
            .setAcceptGiftValue(411854L)));
    TeamPolicyManager policyManager = new TeamPolicyManager()
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setPolicy(List.of(new TeamPolicy()
            .setLevel(1)
            .setTarget(400000L)
            .setOnlineTime(0L)
            .setEffectiveDay(0)
            .setMemberSalary(new BigDecimal("1.00"))
            .setOwnSalary(new BigDecimal("0.25"))
            .setTotalSalary(new BigDecimal("1.25"))));

    when(teamMemberTargetService.listManualSalaryTargets(eq(sysOrigin), eq(countryCode), anySet(),
        eq(billBelong))).thenReturn(List.of(historyTarget, activeTarget));
    when(teamMemberService.mapByMemberIds(anySet())).thenReturn(Map.of(userId,
        new TeamMember().setMemberId(userId).setTeamId(teamId)));
    when(teamProfileService.mapProfileByIds(anySet())).thenReturn(Map.of(teamId,
        new TeamProfile().setId(teamId).setOwnUserId(userId).setRegion(region)));
    when(teamSalaryPaymentService.listByPaymentOrRecipientUserIdsAndDateNumbers(eq(sysOrigin),
        anySet(), anySet())).thenReturn(List.of());
    when(teamPolicyManagerService.getReleaseByRegionAndType(sysOrigin, region, null, countryCode))
        .thenReturn(policyManager);
    when(userBankBalanceClient.getBalance(userId))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)));

    List<?> rows = ReflectionTestUtils.invokeMethod(service, "buildRows", sysOrigin, countryCode,
        Set.of(userId), billBelong);
    Object dto = ReflectionTestUtils.invokeMethod(rows.get(0), "getDto");

    assertEquals(1, rows.size());
    assertEquals("active-target", ReflectionTestUtils.getField(dto, "id"));
    assertEquals(new BigDecimal("1.25"), ReflectionTestUtils.getField(dto, "payableSalary"));
  }

  @Test
  void payShouldReturnTopUpDetailsWhenCurrentLevelRecordExistsButSalaryIsInsufficient() {
    RedisService redisService = mock(RedisService.class);
    TeamMemberService teamMemberService = mock(TeamMemberService.class);
    TeamProfileService teamProfileService = mock(TeamProfileService.class);
    UserBankBalanceClient userBankBalanceClient = mock(UserBankBalanceClient.class);
    TeamMemberTargetService teamMemberTargetService = mock(TeamMemberTargetService.class);
    TeamSalaryPaymentService teamSalaryPaymentService = mock(TeamSalaryPaymentService.class);
    TeamPolicyManagerService teamPolicyManagerService = mock(TeamPolicyManagerService.class);
    UserBankRunningWaterClient userBankRunningWaterClient = mock(UserBankRunningWaterClient.class);
    TeamManualSalaryPaymentClientServiceImpl service = new TeamManualSalaryPaymentClientServiceImpl(
        redisService, teamMemberService, teamProfileService, userBankBalanceClient,
        teamMemberTargetService, teamSalaryPaymentService, teamPolicyManagerService,
        userBankRunningWaterClient);

    String sysOrigin = "LIKEI";
    String countryCode = "PH";
    String region = "2046067717605224449";
    Long userId = 2054591808909930498L;
    Long teamId = 2054614353419759617L;
    Integer billBelong = 202605;
    String paymentId = userId + "_" + billBelong;
    TeamMemberTarget target = new TeamMemberTarget()
        .setId("target-id")
        .setTimeId(2056024761217118210L)
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setTeamId(teamId)
        .setUserId(userId)
        .setBillBelong(billBelong)
        .setDailyTargets(List.of(new TeamMemberTargetIndex()
            .setOwnOnlineTime(717L)
            .setAcceptGiftValue(15151819L)));
    TeamSalaryPayment existingPayment = new TeamSalaryPayment()
        .setId(paymentId)
        .setUserId(userId)
        .setDateNumber(billBelong)
        .setSalarySendDetails(List.of(
            new TeamSalaryPaymentDetails()
                .setTeamMemberId(userId)
                .setTeamOwnId(userId)
                .setTeamRegionId(region)
                .setPolicyLevel(6)
                .setAnchor(Boolean.TRUE)
                .setSalary(new BigDecimal("22.00"))
                .setRecycled(Boolean.FALSE),
            new TeamSalaryPaymentDetails()
                .setTeamMemberId(userId)
                .setTeamOwnId(userId)
                .setTeamRegionId(region)
                .setPolicyLevel(6)
                .setAnchor(Boolean.FALSE)
                .setSalary(new BigDecimal("9.50"))
                .setRecycled(Boolean.FALSE)));
    TeamPolicyManager policyManager = new TeamPolicyManager()
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setPolicy(List.of(new TeamPolicy()
            .setLevel(6)
            .setTarget(8810000L)
            .setOnlineTime(10L)
            .setEffectiveDay(0)
            .setMemberSalary(new BigDecimal("35.00"))
            .setOwnSalary(new BigDecimal("8.75"))
            .setTotalSalary(new BigDecimal("43.75"))));

    when(redisService.lock(anyString(), anyLong())).thenReturn(true);
    when(teamMemberTargetService.listManualSalaryTargets(eq(sysOrigin), eq(countryCode), anySet(),
        eq(billBelong))).thenReturn(List.of(target));
    when(teamMemberService.mapByMemberIds(anySet())).thenReturn(Map.of(userId,
        new TeamMember().setMemberId(userId).setTeamId(teamId)));
    when(teamProfileService.mapProfileByIds(anySet())).thenReturn(Map.of(teamId,
        new TeamProfile().setId(teamId).setOwnUserId(userId).setRegion(region)));
    when(teamSalaryPaymentService.listByPaymentOrRecipientUserIdsAndDateNumbers(eq(sysOrigin),
        anySet(), anySet())).thenReturn(List.of(existingPayment));
    when(teamPolicyManagerService.getReleaseByRegionAndType(sysOrigin, region, null, countryCode))
        .thenReturn(policyManager);
    when(teamSalaryPaymentService.createIfAbsent(userId, billBelong, sysOrigin))
        .thenReturn(new TeamSalaryPayment().setId(paymentId));
    when(userBankBalanceClient.getBalance(userId))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(1300L)));
    when(userBankBalanceClient.incr(any())).thenReturn(ResultResponse.success(Boolean.TRUE));
    when(userBankRunningWaterClient.add(any())).thenReturn(ResultResponse.success());
    when(teamSalaryPaymentService.mapPolicy(paymentId)).thenReturn(new HashMap<>());

    List<TeamManualSalaryPaymentResultDTO> results = service.pay(new TeamManualSalaryPaymentCmd()
        .setSysOrigin(sysOrigin)
        .setCountryCode(countryCode)
        .setBillBelong(billBelong)
        .setUserIds(List.of(userId)));

    assertEquals(1, results.size());
    assertEquals(userId, results.get(0).getUserId());
    assertEquals(new BigDecimal("13.00"), results.get(0).getIssuedSalary());
    ArgumentCaptor<TeamSalaryPaymentDetails> detailsCaptor =
        ArgumentCaptor.forClass(TeamSalaryPaymentDetails.class);
    verify(teamSalaryPaymentService).paymentSalary(eq(paymentId), detailsCaptor.capture());
    assertEquals(new BigDecimal("13.00"), detailsCaptor.getValue().getSalary());
    assertEquals(Boolean.TRUE, detailsCaptor.getValue().getAnchor());
    verify(teamSalaryPaymentService, never()).checkRepeatPayment(anyString(), anyLong(),
        anyString(), any(), any());
  }

  @Test
  void payShouldIssueMemberSalaryToAnchorAndAgentSalaryToTeamOwner() {
    RedisService redisService = mock(RedisService.class);
    TeamMemberService teamMemberService = mock(TeamMemberService.class);
    TeamProfileService teamProfileService = mock(TeamProfileService.class);
    UserBankBalanceClient userBankBalanceClient = mock(UserBankBalanceClient.class);
    TeamMemberTargetService teamMemberTargetService = mock(TeamMemberTargetService.class);
    TeamSalaryPaymentService teamSalaryPaymentService = mock(TeamSalaryPaymentService.class);
    TeamPolicyManagerService teamPolicyManagerService = mock(TeamPolicyManagerService.class);
    UserBankRunningWaterClient userBankRunningWaterClient = mock(UserBankRunningWaterClient.class);
    TeamManualSalaryPaymentClientServiceImpl service = new TeamManualSalaryPaymentClientServiceImpl(
        redisService, teamMemberService, teamProfileService, userBankBalanceClient,
        teamMemberTargetService, teamSalaryPaymentService, teamPolicyManagerService,
        userBankRunningWaterClient);

    String sysOrigin = "LIKEI";
    String countryCode = "PH";
    String region = "2046067717605224449";
    Long anchorUserId = 2054591808909930498L;
    Long ownerUserId = 2057118411766427649L;
    Long teamId = 2054614353419759617L;
    Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
    String anchorPaymentId = anchorUserId + "_" + billBelong;
    String ownerPaymentId = ownerUserId + "_" + billBelong;
    TeamMemberTarget target = new TeamMemberTarget()
        .setId("target-id")
        .setTimeId(2056024761217118210L)
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setTeamId(teamId)
        .setUserId(anchorUserId)
        .setBillBelong(billBelong)
        .setDailyTargets(List.of(new TeamMemberTargetIndex()
            .setOwnOnlineTime(717L)
            .setAcceptGiftValue(15151819L)));
    TeamPolicyManager policyManager = new TeamPolicyManager()
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setPolicy(List.of(new TeamPolicy()
            .setLevel(6)
            .setTarget(8810000L)
            .setOnlineTime(10L)
            .setEffectiveDay(0)
            .setMemberSalary(new BigDecimal("15.00"))
            .setOwnSalary(new BigDecimal("3.75"))
            .setTotalSalary(new BigDecimal("18.75"))));

    when(redisService.lock(anyString(), anyLong())).thenReturn(true);
    when(teamMemberTargetService.listManualSalaryTargets(eq(sysOrigin), eq(countryCode), anySet(),
        eq(billBelong))).thenReturn(List.of(target));
    when(teamMemberService.mapByMemberIds(anySet())).thenReturn(Map.of(anchorUserId,
        new TeamMember().setMemberId(anchorUserId).setTeamId(teamId)
            .setRole(TeamMemberRole.MEMBER)));
    when(teamProfileService.mapProfileByIds(anySet())).thenReturn(Map.of(teamId,
        new TeamProfile().setId(teamId).setOwnUserId(ownerUserId).setRegion(region)));
    when(teamSalaryPaymentService.listByPaymentOrRecipientUserIdsAndDateNumbers(eq(sysOrigin),
        anySet(), anySet())).thenReturn(List.of());
    when(teamPolicyManagerService.getReleaseByRegionAndType(sysOrigin, region, null, countryCode))
        .thenReturn(policyManager);
    when(teamSalaryPaymentService.createIfAbsent(anchorUserId, billBelong, sysOrigin))
        .thenReturn(new TeamSalaryPayment().setId(anchorPaymentId));
    when(teamSalaryPaymentService.createIfAbsent(ownerUserId, billBelong, sysOrigin))
        .thenReturn(new TeamSalaryPayment().setId(ownerPaymentId));
    when(userBankBalanceClient.getBalance(anchorUserId))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(1500L)));
    when(userBankBalanceClient.getBalance(ownerUserId))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(375L)));
    when(userBankBalanceClient.incr(any())).thenReturn(ResultResponse.success(Boolean.TRUE));
    when(userBankRunningWaterClient.add(any())).thenReturn(ResultResponse.success());
    when(teamSalaryPaymentService.mapPolicy(anchorPaymentId)).thenReturn(new HashMap<>());
    when(teamSalaryPaymentService.mapPolicy(ownerPaymentId)).thenReturn(new HashMap<>());

    List<TeamManualSalaryPaymentResultDTO> results = service.pay(new TeamManualSalaryPaymentCmd()
        .setSysOrigin(sysOrigin)
        .setCountryCode(countryCode)
        .setUserIds(List.of(anchorUserId)));

    assertEquals(2, results.size());
    assertEquals(new BigDecimal("15.00"), results.stream()
        .filter(result -> Objects.equals(result.getUserId(), anchorUserId))
        .findFirst()
        .orElseThrow()
        .getIssuedSalary());
    assertEquals(new BigDecimal("3.75"), results.stream()
        .filter(result -> Objects.equals(result.getUserId(), ownerUserId))
        .findFirst()
        .orElseThrow()
        .getIssuedSalary());

    ArgumentCaptor<TeamSalaryPaymentDetails> anchorDetailsCaptor =
        ArgumentCaptor.forClass(TeamSalaryPaymentDetails.class);
    verify(teamSalaryPaymentService).paymentSalary(eq(anchorPaymentId),
        anchorDetailsCaptor.capture());
    assertEquals(anchorUserId, anchorDetailsCaptor.getValue().getTeamMemberId());
    assertEquals(Boolean.TRUE, anchorDetailsCaptor.getValue().getAnchor());

    ArgumentCaptor<TeamSalaryPaymentDetails> ownerDetailsCaptor =
        ArgumentCaptor.forClass(TeamSalaryPaymentDetails.class);
    verify(teamSalaryPaymentService).paymentSalary(eq(ownerPaymentId),
        ownerDetailsCaptor.capture());
    assertEquals(anchorUserId, ownerDetailsCaptor.getValue().getTeamMemberId());
    assertEquals(ownerUserId, ownerDetailsCaptor.getValue().getTeamOwnId());
    assertEquals(Boolean.FALSE, ownerDetailsCaptor.getValue().getAnchor());
    assertEquals(new BigDecimal("3.75"), ownerDetailsCaptor.getValue().getSalary());
  }

  @Test
  void payShouldNotRepeatAgentSalaryWhenTargetEntitlementAlreadyPaid() {
    RedisService redisService = mock(RedisService.class);
    TeamMemberService teamMemberService = mock(TeamMemberService.class);
    TeamProfileService teamProfileService = mock(TeamProfileService.class);
    UserBankBalanceClient userBankBalanceClient = mock(UserBankBalanceClient.class);
    TeamMemberTargetService teamMemberTargetService = mock(TeamMemberTargetService.class);
    TeamSalaryPaymentService teamSalaryPaymentService = mock(TeamSalaryPaymentService.class);
    TeamPolicyManagerService teamPolicyManagerService = mock(TeamPolicyManagerService.class);
    UserBankRunningWaterClient userBankRunningWaterClient = mock(UserBankRunningWaterClient.class);
    TeamManualSalaryPaymentClientServiceImpl service = new TeamManualSalaryPaymentClientServiceImpl(
        redisService, teamMemberService, teamProfileService, userBankBalanceClient,
        teamMemberTargetService, teamSalaryPaymentService, teamPolicyManagerService,
        userBankRunningWaterClient);

    String sysOrigin = "LIKEI";
    String countryCode = "PH";
    String region = "2046067717605224449";
    Long anchorUserId = 2054591808909930498L;
    Long ownerUserId = 2057118411766427649L;
    Long teamId = 2054614353419759617L;
    Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
    String ownerPaymentId = ownerUserId + "_" + billBelong;
    TeamMemberTarget target = new TeamMemberTarget()
        .setId("target-id")
        .setTimeId(2056024761217118210L)
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setTeamId(teamId)
        .setUserId(anchorUserId)
        .setBillBelong(billBelong)
        .setDailyTargets(List.of(new TeamMemberTargetIndex()
            .setOwnOnlineTime(717L)
            .setAcceptGiftValue(15151819L)));
    TeamSalaryPayment ownerPayment = new TeamSalaryPayment()
        .setId(ownerPaymentId)
        .setUserId(ownerUserId)
        .setDateNumber(billBelong)
        .setSalarySendDetails(List.of(new TeamSalaryPaymentDetails()
            .setTeamOwnId(ownerUserId)
            .setTeamMemberId(anchorUserId)
            .setTeamRegionId(region)
            .setPolicyLevel(6)
            .setAnchor(Boolean.FALSE)
            .setSalary(new BigDecimal("3.75"))
            .setRecycled(Boolean.FALSE)));
    TeamPolicyManager policyManager = new TeamPolicyManager()
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setPolicy(List.of(new TeamPolicy()
            .setLevel(6)
            .setTarget(8810000L)
            .setOnlineTime(10L)
            .setEffectiveDay(0)
            .setMemberSalary(BigDecimal.ZERO.setScale(2))
            .setOwnSalary(new BigDecimal("3.75"))
            .setTotalSalary(new BigDecimal("3.75"))));

    when(redisService.lock(anyString(), anyLong())).thenReturn(true);
    when(teamMemberTargetService.listManualSalaryTargets(eq(sysOrigin), eq(countryCode), anySet(),
        eq(billBelong))).thenReturn(List.of(target));
    when(teamMemberService.mapByMemberIds(anySet())).thenReturn(Map.of(anchorUserId,
        new TeamMember().setMemberId(anchorUserId).setTeamId(teamId)
            .setRole(TeamMemberRole.MEMBER)));
    when(teamProfileService.mapProfileByIds(anySet())).thenReturn(Map.of(teamId,
        new TeamProfile().setId(teamId).setOwnUserId(ownerUserId).setRegion(region)));
    when(teamSalaryPaymentService.listByPaymentOrRecipientUserIdsAndDateNumbers(eq(sysOrigin),
        anySet(), anySet())).thenReturn(List.of(ownerPayment));
    when(teamPolicyManagerService.getReleaseByRegionAndType(sysOrigin, region, null, countryCode))
        .thenReturn(policyManager);
    when(userBankBalanceClient.getBalance(anchorUserId))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)));

    List<TeamManualSalaryPaymentResultDTO> results = service.pay(new TeamManualSalaryPaymentCmd()
        .setSysOrigin(sysOrigin)
        .setCountryCode(countryCode)
        .setUserIds(List.of(anchorUserId)));

    assertEquals(0, results.size());
    verify(teamSalaryPaymentService, never()).createIfAbsent(anyLong(), any(), anyString());
    verify(teamSalaryPaymentService, never()).paymentSalary(anyString(), any());
  }

  @Test
  void payShouldNotRepeatTargetEntitlementsAlreadyPaidInTargetPaymentDoc() {
    RedisService redisService = mock(RedisService.class);
    TeamMemberService teamMemberService = mock(TeamMemberService.class);
    TeamProfileService teamProfileService = mock(TeamProfileService.class);
    UserBankBalanceClient userBankBalanceClient = mock(UserBankBalanceClient.class);
    TeamMemberTargetService teamMemberTargetService = mock(TeamMemberTargetService.class);
    TeamSalaryPaymentService teamSalaryPaymentService = mock(TeamSalaryPaymentService.class);
    TeamPolicyManagerService teamPolicyManagerService = mock(TeamPolicyManagerService.class);
    UserBankRunningWaterClient userBankRunningWaterClient = mock(UserBankRunningWaterClient.class);
    TeamManualSalaryPaymentClientServiceImpl service = new TeamManualSalaryPaymentClientServiceImpl(
        redisService, teamMemberService, teamProfileService, userBankBalanceClient,
        teamMemberTargetService, teamSalaryPaymentService, teamPolicyManagerService,
        userBankRunningWaterClient);

    String sysOrigin = "LIKEI";
    String countryCode = "PH";
    String region = "2046067717605224449";
    Long userId = 2052023053021409281L;
    Long ownerUserId = 2051169779338768386L;
    Long teamId = 2051672510944047106L;
    Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
    String paymentId = userId + "_" + billBelong;
    TeamMemberTarget target = manualSalaryTarget(sysOrigin, countryCode, region, userId, teamId,
        billBelong);
    TeamSalaryPayment existingPayment = new TeamSalaryPayment()
        .setId(paymentId)
        .setUserId(userId)
        .setDateNumber(billBelong)
        .setSalarySendDetails(List.of(
            new TeamSalaryPaymentDetails()
                .setTeamMemberId(userId)
                .setTeamOwnId(ownerUserId)
                .setTeamRegionId(region)
                .setPolicyLevel(4)
                .setAnchor(Boolean.TRUE)
                .setSalary(new BigDecimal("8.00"))
                .setRecycled(Boolean.FALSE),
            new TeamSalaryPaymentDetails()
                .setTeamMemberId(userId)
                .setTeamOwnId(ownerUserId)
                .setTeamRegionId(region)
                .setPolicyLevel(4)
                .setAnchor(Boolean.FALSE)
                .setSalary(new BigDecimal("2.00"))
                .setRecycled(Boolean.FALSE)));
    TeamPolicyManager policyManager = manualSalaryPolicy(sysOrigin, countryCode, region);

    when(redisService.lock(anyString(), anyLong())).thenReturn(true);
    when(teamMemberTargetService.listManualSalaryTargets(eq(sysOrigin), eq(countryCode), anySet(),
        eq(billBelong))).thenReturn(List.of(target));
    when(teamMemberService.mapByMemberIds(anySet())).thenReturn(Map.of(userId,
        new TeamMember().setMemberId(userId).setTeamId(teamId)
            .setRole(TeamMemberRole.MEMBER)));
    when(teamProfileService.mapProfileByIds(anySet())).thenReturn(Map.of(teamId,
        new TeamProfile().setId(teamId).setOwnUserId(ownerUserId).setRegion(region)));
    when(teamSalaryPaymentService.listByPaymentOrRecipientUserIdsAndDateNumbers(eq(sysOrigin),
        anySet(), anySet())).thenReturn(List.of(existingPayment));
    when(teamPolicyManagerService.getReleaseByRegionAndType(sysOrigin, region, null, countryCode))
        .thenReturn(policyManager);
    when(userBankBalanceClient.getBalance(userId))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)));

    List<TeamManualSalaryPaymentResultDTO> results = service.pay(new TeamManualSalaryPaymentCmd()
        .setSysOrigin(sysOrigin)
        .setCountryCode(countryCode)
        .setUserIds(List.of(userId)));

    assertEquals(0, results.size());
    verify(teamSalaryPaymentService, never()).createIfAbsent(anyLong(), any(), anyString());
    verify(userBankBalanceClient, never()).incr(any());
    verify(userBankRunningWaterClient, never()).add(any());
    verify(teamSalaryPaymentService, never()).paymentSalary(anyString(), any());
  }

  @Test
  void payShouldIssueMemberAndAgentSalaryWhenAnchorIsTeamOwner() {
    RedisService redisService = mock(RedisService.class);
    TeamMemberService teamMemberService = mock(TeamMemberService.class);
    TeamProfileService teamProfileService = mock(TeamProfileService.class);
    UserBankBalanceClient userBankBalanceClient = mock(UserBankBalanceClient.class);
    TeamMemberTargetService teamMemberTargetService = mock(TeamMemberTargetService.class);
    TeamSalaryPaymentService teamSalaryPaymentService = mock(TeamSalaryPaymentService.class);
    TeamPolicyManagerService teamPolicyManagerService = mock(TeamPolicyManagerService.class);
    UserBankRunningWaterClient userBankRunningWaterClient = mock(UserBankRunningWaterClient.class);
    TeamManualSalaryPaymentClientServiceImpl service = new TeamManualSalaryPaymentClientServiceImpl(
        redisService, teamMemberService, teamProfileService, userBankBalanceClient,
        teamMemberTargetService, teamSalaryPaymentService, teamPolicyManagerService,
        userBankRunningWaterClient);

    String sysOrigin = "LIKEI";
    String countryCode = "PH";
    String region = "2046067717605224449";
    Long userId = 2052023053021409281L;
    Long teamId = 2051672510944047106L;
    Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
    String paymentId = userId + "_" + billBelong;
    TeamMemberTarget target = manualSalaryTarget(sysOrigin, countryCode, region, userId, teamId,
        billBelong);
    TeamPolicyManager policyManager = manualSalaryPolicy(sysOrigin, countryCode, region);

    when(redisService.lock(anyString(), anyLong())).thenReturn(true);
    when(teamMemberTargetService.listManualSalaryTargets(eq(sysOrigin), eq(countryCode), anySet(),
        eq(billBelong))).thenReturn(List.of(target));
    when(teamMemberService.mapByMemberIds(anySet())).thenReturn(Map.of(userId,
        new TeamMember().setMemberId(userId).setTeamId(teamId).setRole(TeamMemberRole.OWN)));
    when(teamProfileService.mapProfileByIds(anySet())).thenReturn(Map.of(teamId,
        new TeamProfile().setId(teamId).setOwnUserId(userId).setRegion(region)));
    when(teamSalaryPaymentService.listByPaymentOrRecipientUserIdsAndDateNumbers(eq(sysOrigin),
        anySet(), anySet())).thenReturn(List.of());
    when(teamPolicyManagerService.getReleaseByRegionAndType(sysOrigin, region, null, countryCode))
        .thenReturn(policyManager);
    when(teamSalaryPaymentService.createIfAbsent(userId, billBelong, sysOrigin))
        .thenReturn(new TeamSalaryPayment().setId(paymentId));
    when(userBankBalanceClient.getBalance(userId))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(800L)))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(800L)))
        .thenReturn(ResultResponse.success(BankBalanceDTO.of(1000L)));
    when(userBankBalanceClient.incr(any())).thenReturn(ResultResponse.success(Boolean.TRUE));
    when(userBankRunningWaterClient.add(any())).thenReturn(ResultResponse.success());
    when(teamSalaryPaymentService.mapPolicy(paymentId)).thenReturn(new HashMap<>());

    List<TeamManualSalaryPaymentResultDTO> results = service.pay(new TeamManualSalaryPaymentCmd()
        .setSysOrigin(sysOrigin)
        .setCountryCode(countryCode)
        .setUserIds(List.of(userId)));

    assertEquals(2, results.size());
    assertEquals(new BigDecimal("8.00"), results.get(0).getIssuedSalary());
    assertEquals(new BigDecimal("2.00"), results.get(1).getIssuedSalary());
    ArgumentCaptor<TeamSalaryPaymentDetails> detailsCaptor =
        ArgumentCaptor.forClass(TeamSalaryPaymentDetails.class);
    verify(teamSalaryPaymentService, org.mockito.Mockito.times(2)).paymentSalary(eq(paymentId),
        detailsCaptor.capture());
    assertEquals(Boolean.TRUE, detailsCaptor.getAllValues().get(0).getAnchor());
    assertEquals(Boolean.FALSE, detailsCaptor.getAllValues().get(1).getAnchor());
  }

  private Object paymentSummary(BigDecimal totalIssuedSalary, BigDecimal memberIssuedSalary,
      BigDecimal agentIssuedSalary) throws Exception {
    Class<?> summaryClass = Class.forName(
        "com.red.circle.other.app.inner.service.team.impl."
            + "TeamManualSalaryPaymentClientServiceImpl$PaymentSummary");
    Constructor<?> constructor = summaryClass.getDeclaredConstructor(
        BigDecimal.class,
        BigDecimal.class,
        BigDecimal.class,
        BigDecimal.class,
        BigDecimal.class,
        BigDecimal.class);
    constructor.setAccessible(true);
    return constructor.newInstance(
        totalIssuedSalary,
        memberIssuedSalary,
        agentIssuedSalary,
        BigDecimal.ZERO.setScale(2),
        BigDecimal.ZERO.setScale(2),
        BigDecimal.ZERO.setScale(2));
  }

  private TeamMemberTarget manualSalaryTarget(String sysOrigin, String countryCode, String region,
      Long userId, Long teamId, Integer billBelong) {
    return new TeamMemberTarget()
        .setId("target-id")
        .setTimeId(1779030072251003137L)
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setTeamId(teamId)
        .setUserId(userId)
        .setBillBelong(billBelong)
        .setDailyTargets(List.of(new TeamMemberTargetIndex()
            .setOwnOnlineTime(4586L)
            .setAcceptGiftValue(2104843L)));
  }

  private TeamPolicyManager manualSalaryPolicy(String sysOrigin, String countryCode,
      String region) {
    return new TeamPolicyManager()
        .setSysOrigin(sysOrigin)
        .setRegion(region)
        .setCountryCode(countryCode)
        .setPolicy(List.of(new TeamPolicy()
            .setLevel(4)
            .setTarget(2000000L)
            .setOnlineTime(10L)
            .setEffectiveDay(0)
            .setMemberSalary(new BigDecimal("8.00"))
            .setOwnSalary(new BigDecimal("2.00"))
            .setTotalSalary(new BigDecimal("10.00"))));
  }
}
