package cn.novalon.manage.sys.core.service.impl;

import cn.novalon.manage.common.util.StatusConstants;
import cn.novalon.manage.sys.config.IntegrationTestConfig;
import cn.novalon.manage.sys.core.domain.SysUser;
import cn.novalon.manage.sys.core.domain.SysRole;
import cn.novalon.manage.sys.core.domain.UserRole;
import cn.novalon.manage.sys.core.repository.ISysUserRepository;
import cn.novalon.manage.sys.core.repository.ISysRoleRepository;
import cn.novalon.manage.sys.core.repository.IUserRoleRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest;
import org.springframework.context.annotation.Import;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.test.StepVerifier;

import java.util.Arrays;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 用户服务集成测试
 * 
 * 使用Testcontainers进行PostgreSQL数据库集成测试
 * 
 * @author 张翔
 * @date 2026-04-02
 */
@DataR2dbcTest
@Testcontainers
@ActiveProfiles("test")
@ContextConfiguration(classes = IntegrationTestConfig.class)
class SysUserServiceIntegrationTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine")
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");

    @DynamicPropertySource
    static void postgresProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.r2dbc.url", () -> String.format("r2dbc:postgresql://%s:%d/%s",
                postgres.getHost(),
                postgres.getFirstMappedPort(),
                postgres.getDatabaseName()));
        registry.add("spring.r2dbc.username", postgres::getUsername);
        registry.add("spring.r2dbc.password", postgres::getPassword);
    }

    @Autowired
    private ISysUserRepository userRepository;

    @Autowired
    private ISysRoleRepository roleRepository;

    @Autowired
    private IUserRoleRepository userRoleRepository;

    @Autowired
    private R2dbcEntityTemplate r2dbcEntityTemplate;

    private SysUserService userService;
    private PasswordEncoder passwordEncoder;

    @BeforeEach
    void setUp() {
        passwordEncoder = new BCryptPasswordEncoder(12);
        userService = new SysUserService(userRepository, roleRepository, userRoleRepository, passwordEncoder);

        r2dbcEntityTemplate.delete(SysUser.class).all().block();
        r2dbcEntityTemplate.delete(SysRole.class).all().block();
        r2dbcEntityTemplate.delete(UserRole.class).all().block();
    }

    @Test
    void testCreateAndFindUser() {
        SysUser user = new SysUser();
        user.setUsername("testuser");
        user.setPassword("password123");
        user.setEmail("test@example.com");
        user.setNickname("Test User");
        user.setPhone("13800138000");

        StepVerifier.create(userService.createUser(user))
                .expectNextMatches(createdUser -> {
                    assertNotNull(createdUser.getId());
                    assertEquals("testuser", createdUser.getUsername());
                    assertEquals("test@example.com", createdUser.getEmail());
                    assertTrue(createdUser.getPassword().startsWith("$2b$"));
                    assertEquals(StatusConstants.ENABLED, createdUser.getStatus());
                    return true;
                })
                .verifyComplete();

        StepVerifier.create(userService.findByUsername("testuser"))
                .expectNextMatches(foundUser -> {
                    assertEquals("testuser", foundUser.getUsername());
                    assertEquals("test@example.com", foundUser.getEmail());
                    return true;
                })
                .verifyComplete();
    }

    @Test
    void testUpdateUser() {
        SysUser user = new SysUser();
        user.setUsername("updateuser");
        user.setPassword("password123");
        user.setEmail("update@example.com");

        SysUser createdUser = userService.createUser(user).block();
        assertNotNull(createdUser);

        createdUser.setEmail("updated@example.com");
        createdUser.setNickname("Updated User");

        StepVerifier.create(userService.updateUser(createdUser))
                .expectNextMatches(updatedUser -> {
                    assertEquals("updated@example.com", updatedUser.getEmail());
                    assertEquals("Updated User", updatedUser.getNickname());
                    return true;
                })
                .verifyComplete();
    }

    @Test
    void testDeleteUser() {
        SysUser user = new SysUser();
        user.setUsername("deleteuser");
        user.setPassword("password123");
        user.setEmail("delete@example.com");

        SysUser createdUser = userService.createUser(user).block();
        assertNotNull(createdUser);

        StepVerifier.create(userService.deleteUser(createdUser.getId()))
                .verifyComplete();

        StepVerifier.create(userService.findById(createdUser.getId()))
                .verifyComplete();
    }

    @Test
    void testChangePassword() {
        SysUser user = new SysUser();
        user.setUsername("pwduser");
        user.setPassword("oldPassword");
        user.setEmail("pwd@example.com");

        SysUser createdUser = userService.createUser(user).block();
        assertNotNull(createdUser);

        StepVerifier.create(userService.changePassword(createdUser.getId(), "oldPassword", "newPassword"))
                .expectNextMatches(updatedUser -> {
                    assertNotEquals(createdUser.getPassword(), updatedUser.getPassword());
                    assertTrue(passwordEncoder.matches("newPassword", updatedUser.getPassword()));
                    return true;
                })
                .verifyComplete();
    }

    @Test
    void testAssignRolesToUser() {
        SysRole role1 = new SysRole();
        role1.setRoleName("Test Role 1");
        role1.setRoleKey("test_role_1");
        role1.setStatus(1);

        SysRole role2 = new SysRole();
        role2.setRoleName("Test Role 2");
        role2.setRoleKey("test_role_2");
        role2.setStatus(1);

        SysRole createdRole1 = roleRepository.save(role1).block();
        SysRole createdRole2 = roleRepository.save(role2).block();
        assertNotNull(createdRole1);
        assertNotNull(createdRole2);

        SysUser user = new SysUser();
        user.setUsername("roleuser");
        user.setPassword("password123");
        user.setEmail("role@example.com");

        SysUser createdUser = userService.createUser(user).block();
        assertNotNull(createdUser);

        StepVerifier.create(userService.assignRolesToUser(createdUser.getId(),
                Arrays.asList(createdRole1.getId(), createdRole2.getId())))
                .verifyComplete();

        StepVerifier.create(userRoleRepository.findByUserId(createdUser.getId()).collectList())
                .expectNextMatches(userRoles -> {
                    assertEquals(2, userRoles.size());
                    return true;
                })
                .verifyComplete();
    }

    @Test
    void testFindAllUsers() {
        for (int i = 1; i <= 3; i++) {
            SysUser user = new SysUser();
            user.setUsername("user" + i);
            user.setPassword("password" + i);
            user.setEmail("user" + i + "@example.com");
            userService.createUser(user).block();
        }

        StepVerifier.create(userService.findAll(false).collectList())
                .expectNextMatches(users -> {
                    assertEquals(3, users.size());
                    return true;
                })
                .verifyComplete();
    }

    @Test
    void testExistsByUsername() {
        SysUser user = new SysUser();
        user.setUsername("existinguser");
        user.setPassword("password123");
        user.setEmail("existing@example.com");
        userService.createUser(user).block();

        StepVerifier.create(userService.existsByUsername("existinguser"))
                .expectNext(true)
                .verifyComplete();

        StepVerifier.create(userService.existsByUsername("nonexistinguser"))
                .expectNext(false)
                .verifyComplete();
    }
}
