package com.red.circle.other.infra.gateway.user;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.red.circle.common.business.enums.AccountStatusEnum;
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.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.rds.service.user.device.DeviceRegisterQuantityService;
import com.red.circle.other.infra.database.rds.service.user.device.IpRegisterQuantityService;
import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService;
import com.red.circle.other.infra.database.rds.service.user.user.ExpandService;
import com.red.circle.tool.core.date.TimestampUtils;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class RegisterDeviceGatewayImplTest {

  private static final Long SELF_ID = 100L;
  private static final Long OTHER_ID = 200L;
  private static final String FINGERPRINT = "Android|Infinix X6871|1.2.3.4";

  private IpRegisterQuantityService ipRegisterQuantityService;
  private DeviceRegisterQuantityService deviceRegisterQuantityService;
  private LatestMobileDeviceService latestMobileDeviceService;
  private UserProfileGateway userProfileGateway;
  private TeamMemberService teamMemberService;
  private EnumConfigCacheService enumConfigCacheService;
  private ExpandService expandService;
  private RegisterDeviceGatewayImpl gateway;

  @BeforeEach
  void setUp() {
    ipRegisterQuantityService = mock(IpRegisterQuantityService.class);
    deviceRegisterQuantityService = mock(DeviceRegisterQuantityService.class);
    latestMobileDeviceService = mock(LatestMobileDeviceService.class);
    userProfileGateway = mock(UserProfileGateway.class);
    teamMemberService = mock(TeamMemberService.class);
    enumConfigCacheService = mock(EnumConfigCacheService.class);
    expandService = mock(ExpandService.class);
    gateway = new RegisterDeviceGatewayImpl(
        ipRegisterQuantityService,
        deviceRegisterQuantityService,
        latestMobileDeviceService,
        userProfileGateway,
        teamMemberService,
        enumConfigCacheService,
        expandService);

    when(expandService.getDeviceListBlock(anyLong())).thenReturn(null);
    when(userProfileGateway.getByUserId(SELF_ID)).thenReturn(profile(SELF_ID, "1001", AccountStatusEnum.NORMAL, 0));
    when(latestMobileDeviceService.getDeviceFingerprintAndIPByUserId(SELF_ID)).thenReturn(FINGERPRINT);
    when(latestMobileDeviceService.listUserIdsByDeviceFingerprint(FINGERPRINT, "LIKEI"))
        .thenReturn(List.of(SELF_ID, OTHER_ID));
    when(teamMemberService.getByMemberId(OTHER_ID)).thenReturn(new TeamMember().setMemberId(OTHER_ID));
  }

  private UserProfile profile(Long id, String account, AccountStatusEnum status, int freezeDays) {
    UserProfile profile = new UserProfile();
    profile.setId(id);
    profile.setAccount(account);
    profile.setOriginSys("LIKEI");
    profile.setAccountStatus(status.name());
    profile.setFreezingTime(freezeDays == 0 ? TimestampUtils.now() : TimestampUtils.nowPlusDays(freezeDays));
    return profile;
  }

  @Test
  void blocksWhenSameDeviceHasActiveTeamMember() {
    when(userProfileGateway.getByUserId(OTHER_ID)).thenReturn(profile(OTHER_ID, "1002", AccountStatusEnum.NORMAL, 0));

    assertTrue(gateway.checkDeviceFingerprintHasAnchor(SELF_ID));
  }

  @Test
  void exemptsArchivedAccountOnSameDevice() {
    when(userProfileGateway.getByUserId(OTHER_ID)).thenReturn(profile(OTHER_ID, "1002", AccountStatusEnum.ARCHIVE, 365));

    assertFalse(gateway.checkDeviceFingerprintHasAnchor(SELF_ID));
  }

  @Test
  void exemptsArchiveDeviceAccountOnSameDevice() {
    when(userProfileGateway.getByUserId(OTHER_ID)).thenReturn(profile(OTHER_ID, "1002", AccountStatusEnum.ARCHIVE_DEVICE, 0));

    assertFalse(gateway.checkDeviceFingerprintHasAnchor(SELF_ID));
  }

  @Test
  void exemptsDeletedAccountOnSameDevice() {
    // 已注销账号 getByUserId 返回 null（逻辑删过滤），不应永久占用设备名额
    when(userProfileGateway.getByUserId(OTHER_ID)).thenReturn(null);

    assertFalse(gateway.checkDeviceFingerprintHasAnchor(SELF_ID));
  }

  @Test
  void expiredArchiveStillBlocks() {
    // ARCHIVE 但冻结期已过 = 已自动解封，重新占用名额
    UserProfile expired = profile(OTHER_ID, "1002", AccountStatusEnum.ARCHIVE, 0);
    when(userProfileGateway.getByUserId(OTHER_ID)).thenReturn(expired);

    assertTrue(gateway.checkDeviceFingerprintHasAnchor(SELF_ID));
  }

  @Test
  void whitelistMatchesExactAccountOnly() {
    when(userProfileGateway.getByUserId(OTHER_ID)).thenReturn(profile(OTHER_ID, "1002", AccountStatusEnum.NORMAL, 0));

    // 子串不再误命中：白名单 51001 不应豁免账号 1001
    when(enumConfigCacheService.getValue(com.red.circle.other.inner.enums.config.EnumConfigKey.DEVICE_REGISTER_WHITELIST, "LIKEI"))
        .thenReturn("51001,9999");
    assertTrue(gateway.checkDeviceFingerprintHasAnchor(SELF_ID));

    // 精确命中豁免
    when(enumConfigCacheService.getValue(com.red.circle.other.inner.enums.config.EnumConfigKey.DEVICE_REGISTER_WHITELIST, "LIKEI"))
        .thenReturn("51001,1001");
    assertFalse(gateway.checkDeviceFingerprintHasAnchor(SELF_ID));
  }

  @Test
  void manualDeviceListOverridesFingerprintLogic() {
    when(expandService.getDeviceListBlock(SELF_ID)).thenReturn(Boolean.TRUE);
    assertTrue(gateway.checkDeviceFingerprintHasAnchor(SELF_ID));

    when(expandService.getDeviceListBlock(SELF_ID)).thenReturn(Boolean.FALSE);
    assertFalse(gateway.checkDeviceFingerprintHasAnchor(SELF_ID));
  }
}
