package cn.novalon.gym.manage.sys.handler.auth;

import cn.novalon.gym.manage.sys.dto.request.LoginRequest;
import cn.novalon.gym.manage.sys.dto.request.UserRegisterRequest;
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
import cn.novalon.gym.manage.sys.core.domain.SysUser;
import cn.novalon.gym.manage.sys.core.domain.SysRole;
import cn.novalon.gym.manage.sys.core.domain.SysLoginLog;
import cn.novalon.gym.manage.sys.util.TestDataFactory;
import cn.novalon.gym.manage.sys.core.service.ISysUserService;
import cn.novalon.gym.manage.sys.core.service.ISysLoginLogService;
import cn.novalon.gym.manage.sys.util.UserAgentParser;
import cn.novalon.gym.manage.sys.util.IpLocationParser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(MockitoExtension.class)
class SysAuthHandlerTest {

    @Mock
    private ISysUserService userService;

    @Mock
    private PasswordEncoder passwordEncoder;

    @Mock
    private JwtTokenProvider jwtTokenProvider;

    @Mock
    private ISysLoginLogService loginLogService;

    @Mock
    private UserAgentParser userAgentParser;

    @Mock
    private IpLocationParser ipLocationParser;

    private SysAuthHandler authHandler;
    private SysUser testUser;

    @BeforeEach
    void setUp() {
        authHandler = new SysAuthHandler(userService, passwordEncoder, jwtTokenProvider, loginLogService,
                userAgentParser, ipLocationParser);

        testUser = TestDataFactory.createTestUser();
    }

    @Test
    void testLogin_Success() {
        LoginRequest loginRequest = TestDataFactory.createLoginRequest();
        
        // 使用BCrypt编码的真实密码
        String rawPassword = "password123";
        org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder encoder = 
            new org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder(12);
        String realEncodedPassword = encoder.encode(rawPassword);
        testUser.setPassword(realEncodedPassword);

        when(userService.findByUsername("testuser")).thenReturn(Mono.just(testUser));
        
        // 配置密码编码器Mock来验证密码
        when(passwordEncoder.matches(rawPassword, realEncodedPassword)).thenReturn(true);
        
        when(jwtTokenProvider.generateToken(eq("testuser"), eq(1L), anyList())).thenReturn("test_token");
        
        // 使用测试数据工厂创建角色
        SysRole mockRole = TestDataFactory.createUserRole();
        
        when(userService.getUserRoles(1L)).thenReturn(Flux.just(mockRole));
        when(loginLogService.save(any())).thenReturn(Mono.just(new SysLoginLog()));

        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(loginRequest));
        Mono<ServerResponse> response = authHandler.login(request);

        StepVerifier.create(response)
                .assertNext(serverResponse -> {
                    System.out.println("Response status: " + serverResponse.statusCode());
                    System.out.println("Response type: " + serverResponse.getClass().getName());
                    
                    // 直接断言响应状态码
                    assertThat(serverResponse.statusCode()).isEqualTo(HttpStatus.OK);
                })
                .verifyComplete();

        verify(userService).findByUsername("testuser");
        verify(jwtTokenProvider).generateToken(eq("testuser"), eq(1L), anyList());
    }

    @Test
    void testLogin_EmptyUsername() {
        LoginRequest loginRequest = new LoginRequest();
        loginRequest.setUsername("");
        loginRequest.setPassword("password123");

        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(loginRequest));
        Mono<ServerResponse> response = authHandler.login(request);

        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.BAD_REQUEST)
                .verifyComplete();
    }

    @Test
    void testLogin_EmptyPassword() {
        LoginRequest loginRequest = new LoginRequest();
        loginRequest.setUsername("testuser");
        loginRequest.setPassword("");

        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(loginRequest));
        Mono<ServerResponse> response = authHandler.login(request);

        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.BAD_REQUEST)
                .verifyComplete();
    }

    @Test
    void testLogin_UserNotFound() {
        LoginRequest loginRequest = new LoginRequest();
        loginRequest.setUsername("unknown");
        loginRequest.setPassword("password123");

        when(userService.findByUsername("unknown")).thenReturn(Mono.empty());

        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(loginRequest));
        Mono<ServerResponse> response = authHandler.login(request);

        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.UNAUTHORIZED)
                .verifyComplete();

        verify(userService).findByUsername("unknown");
    }

    @Test
    void testLogin_WrongPassword() {
        LoginRequest loginRequest = TestDataFactory.createLoginRequest();
        loginRequest.setPassword("wrongpassword");

        when(userService.findByUsername("testuser")).thenReturn(Mono.just(testUser));
        when(passwordEncoder.matches("wrongpassword", testUser.getPassword())).thenReturn(false);

        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(loginRequest));
        Mono<ServerResponse> response = authHandler.login(request);

        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.UNAUTHORIZED)
                .verifyComplete();

        verify(userService).findByUsername("testuser");
        verify(passwordEncoder).matches("wrongpassword", testUser.getPassword());
    }

    @Test
    void testLogin_UserDisabled() {
        testUser.setStatus(0);

        LoginRequest loginRequest = TestDataFactory.createLoginRequest();

        when(userService.findByUsername("testuser")).thenReturn(Mono.just(testUser));
        when(passwordEncoder.matches("password123", testUser.getPassword())).thenReturn(true);

        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(loginRequest));
        Mono<ServerResponse> response = authHandler.login(request);

        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.UNAUTHORIZED)
                .verifyComplete();

        verify(userService).findByUsername("testuser");
        verify(passwordEncoder).matches("password123", testUser.getPassword());
    }

    @Test
    void testRegister_Success() {
        UserRegisterRequest registerRequest = new UserRegisterRequest();
        registerRequest.setUsername("newuser");
        registerRequest.setPassword("password123");
        registerRequest.setEmail("new@example.com");

        when(userService.findByUsername("newuser")).thenReturn(Mono.empty());
        when(passwordEncoder.encode("password123")).thenReturn("encoded_password");
        when(userService.createUser(any(SysUser.class))).thenReturn(Mono.just(testUser));

        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(registerRequest));
        Mono<ServerResponse> response = authHandler.register(request);

        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.CREATED)
                .verifyComplete();

        verify(userService).findByUsername("newuser");
        verify(passwordEncoder).encode("password123");
        verify(userService).createUser(any(SysUser.class));
    }

    @Test
    void testRegister_UsernameExists() {
        UserRegisterRequest registerRequest = new UserRegisterRequest();
        registerRequest.setUsername("testuser");
        registerRequest.setPassword("password123");
        registerRequest.setEmail("new@example.com");

        when(userService.findByUsername("testuser")).thenReturn(Mono.just(testUser));

        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(registerRequest));
        Mono<ServerResponse> response = authHandler.register(request);

        StepVerifier.create(response)
                .expectErrorMatches(ex -> ex.getMessage().contains("用户名已存在"))
                .verify();

        verify(userService).findByUsername("testuser");
    }

    @Test
    void testLogout() {
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = authHandler.logout(request);

        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
    }
}