package com.cf.imes.module.system.service.tokenconfig;

import cn.hutool.core.collection.CollUtil;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest;
import com.cf.imes.module.system.framework.jwt.config.JwtProperties;
import com.cf.imes.module.system.controller.admin.tokenconfig.vo.TokenConfigPageReqVO;
import com.cf.imes.module.system.controller.admin.tokenconfig.vo.TokenConfigRespVO;
import com.cf.imes.module.system.controller.admin.tokenconfig.vo.TokenConfigSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
import com.cf.imes.module.system.dal.dataobject.tokenconfig.TokenConfigDO;
import com.cf.imes.module.system.dal.mysql.organ.OrganMapper;
import com.cf.imes.module.system.dal.mysql.tokenconfig.TokenConfigMapper;
import com.cf.imes.module.system.enums.token.TokenConfigAppTypeEnum;
import com.cf.imes.module.system.enums.token.TokenExpiresTimeTypeEnum;
import com.cf.imes.module.system.service.organ.OrganService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;


import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORGAN_DISABLE;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORGAN_EXPIRE;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORGAN_NOT_EXISTS;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.TOKEN_APP_TYPE_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.TOKEN_APP_TYPE_NO_UPDATE;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.TOKEN_CONFIG_NOT_EXISTS;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.TOKEN_DATA_IS_NULL;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.TOKEN_EXPIRES_TIME_DATA_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.TOKEN_EXPIRES_TIME_IS_NULL;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.TOKEN_TIME_IS_EXPIRES;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@Import(TokenConfigServiceImpl.class)
class TokenConfigServiceImplTest extends BaseDbAndRedisUnitTest {

    @Resource
    private TokenConfigServiceImpl tokenConfigService;

    @Resource
    private TokenConfigMapper tokenConfigMapper;

    @MockitoBean
    private OrganMapper organMapper;

    @MockitoBean
    private OrganService organService;

    @MockitoBean
    private JwtProperties jwtProperties;

    @BeforeEach
    void setUp() {
        OrganizationDO organ = new OrganizationDO();
        organ.setId(1L);
        organ.setContactMobile("13800000000");
        organ.setName("测试组织");

        when(organService.validOrgan(1L)).thenReturn(organ);
        when(jwtProperties.getSecret()).thenReturn("BjEM0tnL3W5zYLQ6pllol49uYVXe+f66pHyOM/tkGWg=");
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void testGetTokenConfigPage_timeTypeNull_andEmptyResult() {
        TokenConfigPageReqVO reqVO = TokenConfigPageReqVO
                .builder()
                .timeType(null)
                .build();

        // mapper 真实返回空（或你准备的测试数据为空）
        PageResult<TokenConfigRespVO> result =
                tokenConfigService.getTokenConfigPage(reqVO);

        assertNotNull(result);
        assertTrue(CollUtil.isEmpty(result.getList()));

        // organService 不应该被调用
        verify(organService, never()).validOrgan(anyLong());
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void testGetTokenConfigPage_timeTypeNotNull_andEmptyResult() {
        TokenConfigPageReqVO reqVO = TokenConfigPageReqVO
                .builder()
                .timeType(new LocalDate[]{
                        LocalDate.of(2025, 1, 1),
                        LocalDate.of(2025, 1, 31)
                })
                .build();

        PageResult<TokenConfigRespVO> result =
                tokenConfigService.getTokenConfigPage(reqVO);

        assertNotNull(result);
        assertTrue(CollUtil.isEmpty(result.getList()));

        verify(organService, never()).validOrgan(anyLong());
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void testGetTokenConfigPage_timeTypeNotNull_andHasData() {
        TokenConfigPageReqVO reqVO = TokenConfigPageReqVO
                .builder()
                .timeType(new LocalDate[]{
                        LocalDate.of(2026, 1, 1),
                        LocalDate.of(2026, 1, 31)
                })
                .build();

        // 准备数据
        TokenConfigDO config = randomPojo(TokenConfigDO.class, o->{
            o.setOrganId(1L);
            o.setAppToken("imes-abcdefg123456");
            o.setExpiresTime(LocalDateTime.now());
        });

        tokenConfigMapper.insert(config);

        PageResult<TokenConfigRespVO> result =
                tokenConfigService.getTokenConfigPage(reqVO);

        assertNotNull(result);
        assertFalse(CollUtil.isEmpty(result.getList()));

        TokenConfigRespVO resp = result.getList().get(0);

        // 组织名已填充
        assertEquals("测试组织", resp.getOrganName());

        // token 已脱敏
        assertNotNull(resp.getAppToken());
        assertTrue(resp.getAppToken().contains("*"));

        verify(organService, times(1))
                .validOrgan(anyLong());
    }

    @Test
    void testGetTokenConfig_notExists() {
        Long notExistsId = -1L; // 确保数据库不存在

        assertServiceException(() -> tokenConfigService.getTokenConConfig(notExistsId),
                TOKEN_CONFIG_NOT_EXISTS);
    }

    @Test
    void testGetTokenConfig_success_andMaskToken() {
        // 1.准备数据
        TokenConfigDO config = randomPojo(TokenConfigDO.class, o->{
            o.setOrganId(1L);
            o.setAppToken("imes-abcdefg123456");
            o.setExpiresTime(LocalDateTime.now());
        });

        tokenConfigMapper.insert(config);
        Long id = config.getId();

        // 2. 调用
        TokenConfigDO result =
                tokenConfigService.getTokenConConfig(id);

        // 3. 断言
        assertNotNull(result);
        assertEquals(id, result.getId());

        // token 已脱敏
        assertNotEquals("imes-abcdefg123456", result.getAppToken());
        assertTrue(result.getAppToken().contains("*"));
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void testInsertTokenConfig_appTypeExists() {
        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());

        tokenConfigMapper.insert(randomPojo(TokenConfigDO.class, o -> {
                    o.setOrganId(1L);
                    o.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
                })
        );

        assertServiceException(() -> tokenConfigService.insertTokenConfig(reqVO),
                TOKEN_APP_TYPE_EXIST);
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void testInsertTokenConfig_customizeTime() {
        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        reqVO.setCustomizeTime("2025-01-01");

        tokenConfigService.insertTokenConfig(reqVO);

        List<Integer> db =
                tokenConfigMapper.selectAppTypeByOrganId(1L);

        assertEquals(1, db.size());
        assertEquals(db.get(0), TokenConfigAppTypeEnum.WEBCAD.getType());
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void testInsertTokenConfig_timeTypeNull() {
        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setAppType(3);

        assertServiceException(() -> tokenConfigService.insertTokenConfig(reqVO),
                TOKEN_EXPIRES_TIME_IS_NULL);
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void testInsertTokenConfig_timeTypeEnum() {
        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        reqVO.setTimeType(TokenExpiresTimeTypeEnum.ONEHOURS.getType());

        tokenConfigService.insertTokenConfig(reqVO);

        List<TokenConfigDO> db =
                tokenConfigMapper.selectList();

        assertEquals(1, db.size());
        assertTrue(db.get(0).getExpiresTime().isAfter(LocalDateTime.now()));
    }

    @Test
    void testUpdateTokenConfig_notExists() {
        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setId(999L);

        assertServiceException(() -> tokenConfigService.updateTokenConfig(reqVO),
                TOKEN_CONFIG_NOT_EXISTS);
    }

    @Test
    void testUpdateTokenConfig_appTypeChanged() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setOrganId(1L);
            o.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        });
        tokenConfigMapper.insert(tokenConfigDO);
        Long id = tokenConfigDO.getId();

                TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setId(id);
        reqVO.setAppType(2); // 改了

        assertServiceException(() -> tokenConfigService.updateTokenConfig(reqVO),
                TOKEN_APP_TYPE_NO_UPDATE);
    }

    @Test
    void testUpdateTokenConfig_customizeTime() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setOrganId(1L);
            o.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        });
        tokenConfigMapper.insert(tokenConfigDO);
        Long id = tokenConfigDO.getId();

        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setId(id);
        reqVO.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        reqVO.setCustomizeTime("2026-01-08");

        tokenConfigService.updateTokenConfig(reqVO);

        TokenConfigDO db = tokenConfigMapper.selectById(id);
        assertEquals(
                LocalDateTime.of(2026,1,8,23,59,59),
                db.getExpiresTime()
        );
    }

    @Test
    void testUpdateTokenConfig_timeType() {
        LocalDateTime createTime = LocalDateTime.now().minusDays(1);
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setOrganId(1L);
            o.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        });
        tokenConfigMapper.insert(tokenConfigDO);
        Long id = tokenConfigDO.getId();

        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setId(id);
        reqVO.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        reqVO.setTimeType(TokenExpiresTimeTypeEnum.ONEMONTHS.getType());

        tokenConfigService.updateTokenConfig(reqVO);

        TokenConfigDO db = tokenConfigMapper.selectById(id);
        assertTrue(db.getExpiresTime().isAfter(createTime));
    }

    @Test
    void testUpdateTokenConfig_expiredTime() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setOrganId(1L);
            o.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        });
        tokenConfigMapper.insert(tokenConfigDO);
        Long id = tokenConfigDO.getId();

        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setId(id);
        reqVO.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        reqVO.setCustomizeTime("2020-01-01");

        assertServiceException(() -> tokenConfigService.updateTokenConfig(reqVO),
                TOKEN_EXPIRES_TIME_DATA_ERROR);
    }

    @Test
    void testUpdateTokenConfig_noTimeChange() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setOrganId(1L);
            o.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        });
        tokenConfigMapper.insert(tokenConfigDO);
        Long id = tokenConfigDO.getId();

        TokenConfigDO before = tokenConfigMapper.selectById(id);

        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setId(id);
        reqVO.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());

        tokenConfigService.updateTokenConfig(reqVO);

        TokenConfigDO after = tokenConfigMapper.selectById(id);
        assertEquals(before.getExpiresTime(), after.getExpiresTime());
    }

    @Test
    void testUpdateTokenConfig_remarkNull() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setOrganId(1L);
            o.setRemark("old");
            o.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        });
        tokenConfigMapper.insert(tokenConfigDO);
        Long id = tokenConfigDO.getId();

        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setId(id);
        reqVO.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());

        tokenConfigService.updateTokenConfig(reqVO);

        assertEquals("old",
                tokenConfigMapper.selectById(id).getRemark());
    }

    @Test
    void testUpdateTokenConfig_remarkUpdate() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setOrganId(1L);
            o.setRemark("old");
            o.setAppType(TokenConfigAppTypeEnum.WEBCAD.getType());
        });
        tokenConfigMapper.insert(tokenConfigDO);
        Long id = tokenConfigDO.getId();

        TokenConfigSaveReqVO reqVO = new TokenConfigSaveReqVO();
        reqVO.setId(id);
        reqVO.setAppType(1);
        reqVO.setRemark("new");

        tokenConfigService.updateTokenConfig(reqVO);

        assertEquals("new",
                tokenConfigMapper.selectById(id).getRemark());
    }

    @Test
    void testDeleteTokenConfig_notExists() {
        assertServiceException(() -> tokenConfigService.deleteTokenConfig(999L),
                TOKEN_DATA_IS_NULL);
    }

    @Test
    void testDeleteTokenConfig_success() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setOrganId(1L);
        });
        tokenConfigMapper.insert(tokenConfigDO);
        Long id = tokenConfigDO.getId();

        // 先确认存在
        assertNotNull(tokenConfigMapper.selectById(id));

        // 删除
        tokenConfigService.deleteTokenConfig(id);

        // 再确认不存在
        assertNull(tokenConfigMapper.selectById(id));
    }

    @Test
    void testCheckTokenConfig_tokenNotExists() {
        assertServiceException(() -> tokenConfigService.checkTokenConfig("imes-not-exists"),
                TOKEN_CONFIG_NOT_EXISTS);
    }

    @Test
    void testCheckTokenConfig_tokenExpired() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setExpiresTime(LocalDateTime.now().minusDays(1));
            o.setAppToken("expired-token");
        });
        tokenConfigMapper.insert(tokenConfigDO);

        assertServiceException(() -> tokenConfigService.checkTokenConfig("imes-expired-token"),
                TOKEN_TIME_IS_EXPIRES);
    }

    @Test
    void testCheckTokenConfig_organNotExists() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setExpiresTime(LocalDateTime.now().plusDays(1));
            o.setAppToken("valid-token");
        });
        tokenConfigMapper.insert(tokenConfigDO);

        when(organMapper.selectById(any())).thenReturn(null);

        assertServiceException(() -> tokenConfigService.checkTokenConfig("imes-valid-token"),
                ORGAN_NOT_EXISTS);
    }

    @Test
    void testCheckTokenConfig_organDisabled() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setExpiresTime(LocalDateTime.now().plusDays(1));
            o.setAppToken("valid-token");
        });
        tokenConfigMapper.insert(tokenConfigDO);

        OrganizationDO disabledOrgan = new OrganizationDO();
        disabledOrgan.setStatus(CommonStatusEnum.DISABLE.getStatus());
        when(organMapper.selectById(any())).thenReturn(disabledOrgan);

        assertServiceException(() -> tokenConfigService.checkTokenConfig("imes-valid-token"),
                ORGAN_DISABLE);
    }

    @Test
    void testCheckTokenConfig_organExpired() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setExpiresTime(LocalDateTime.now().plusDays(1));
            o.setAppToken("valid-token");
        });
        tokenConfigMapper.insert(tokenConfigDO);

        OrganizationDO disabledOrgan = new OrganizationDO();
        disabledOrgan.setStatus(CommonStatusEnum.ENABLE.getStatus());
        disabledOrgan.setExpireTime(LocalDateTime.now().minusDays(1));
        when(organMapper.selectById(any())).thenReturn(disabledOrgan);

        assertServiceException(() -> tokenConfigService.checkTokenConfig("imes-valid-token"),
                ORGAN_EXPIRE);
    }

    @Test
    void testCheckTokenConfig_success() {
        TokenConfigDO tokenConfigDO = randomPojo(TokenConfigDO.class, o -> {
            o.setOrganId(1L);
            o.setAppToken("valid-token");
        });
        tokenConfigMapper.insert(tokenConfigDO);

        OrganizationDO disabledOrgan = new OrganizationDO();
        disabledOrgan.setStatus(CommonStatusEnum.ENABLE.getStatus());
        disabledOrgan.setExpireTime(LocalDateTime.now().plusDays(1));
        when(organMapper.selectById(any())).thenReturn(disabledOrgan);

        Long result = tokenConfigService.checkTokenConfig("imes-valid-token");

        assertEquals(1L, result);
    }

    @Test
    void testTokenConfigCopy_notExists() {
        assertServiceException(() -> tokenConfigService.tokenConfigCopy(999L),
                TOKEN_CONFIG_NOT_EXISTS);
    }

    @Test
    void testTokenConfigCopy_success() {
        TokenConfigDO token = randomPojo(TokenConfigDO.class, o -> {
            o.setAppToken("abc123");
        });
        tokenConfigMapper.insert(token);

        String result = tokenConfigService.tokenConfigCopy(token.getId());

        assertEquals("imes-abc123", result);
    }
}