package cn.novalon.manage.gateway.service.impl;

import cn.novalon.manage.gateway.model.Permission;
import cn.novalon.manage.gateway.model.Role;
import cn.novalon.manage.gateway.model.User;
import cn.novalon.manage.gateway.service.PermissionService;
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.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class PermissionServiceImplTest {

    @Mock
    private WebClient.Builder webClientBuilder;

    @Mock
    private WebClient webClient;

    @Mock
    private WebClient.RequestHeadersUriSpec<?> requestHeadersUriSpec;

    @Mock
    private WebClient.RequestHeadersSpec<?> requestHeadersSpec;

    @Mock
    private WebClient.ResponseSpec responseSpec;

    private PermissionService permissionService;

    @BeforeEach
    void setUp() {
        doReturn(webClient).when(webClientBuilder).build();
        doReturn(requestHeadersUriSpec).when(webClient).get();
        doReturn(requestHeadersSpec).when(requestHeadersUriSpec).uri(anyString());
        doReturn(responseSpec).when(requestHeadersSpec).retrieve();
        permissionService = new PermissionServiceImpl(webClientBuilder, "http://localhost:8084");
    }

    @Test
    void testGetUserById_Success() {
        User expectedUser = new User(1L, "testuser", "test@example.com", "1234567890", 1, System.currentTimeMillis(), System.currentTimeMillis());
        
        doReturn(Mono.just(expectedUser)).when(responseSpec).bodyToMono(eq(User.class));

        User user = permissionService.getUserById(1L);
        
        assertNotNull(user);
        assertEquals("testuser", user.getUsername());
        verify(webClient).get();
    }

    @Test
    void testGetUserById_NullUserId() {
        User user = permissionService.getUserById(null);
        
        assertNull(user);
        verify(webClient, never()).get();
    }

    @Test
    void testGetUserRoles_Success() {
        List<Role> expectedRoles = Arrays.asList(
                new Role(1L, "ADMIN", "Administrator", "Admin role", 1, System.currentTimeMillis(), System.currentTimeMillis()),
                new Role(2L, "USER", "User", "User role", 1, System.currentTimeMillis(), System.currentTimeMillis())
        );
        
        doReturn(Mono.just(expectedRoles.toArray(new Role[0]))).when(responseSpec).bodyToMono(eq(Role[].class));

        List<Role> roles = permissionService.getUserRoles(1L);
        
        assertNotNull(roles);
        assertEquals(2, roles.size());
        verify(webClient).get();
    }

    @Test
    void testGetUserRoles_NullUserId() {
        List<Role> roles = permissionService.getUserRoles(null);
        
        assertNotNull(roles);
        assertTrue(roles.isEmpty());
        verify(webClient, never()).get();
    }

    @Test
    void testGetUserPermissions_Success() {
        Set<Permission> expectedPermissions = new HashSet<>(Arrays.asList(
                new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis()),
                new Permission(2L, "user:write", "Write User", "API", "/api/users/**", "POST", "Write user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
        ));
        
        doReturn(Mono.just(expectedPermissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));

        Set<Permission> permissions = permissionService.getUserPermissions(1L);
        
        assertNotNull(permissions);
        assertEquals(2, permissions.size());
        verify(webClient).get();
    }

    @Test
    void testGetUserPermissions_NullUserId() {
        Set<Permission> permissions = permissionService.getUserPermissions(null);
        
        assertNotNull(permissions);
        assertTrue(permissions.isEmpty());
        verify(webClient, never()).get();
    }

    @Test
    void testHasPermission_True() {
        Set<Permission> permissions = new HashSet<>(Arrays.asList(
                new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
        ));
        
        doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));

        boolean hasPermission = permissionService.hasPermission(1L, "/api/users/123", "GET");
        
        assertTrue(hasPermission);
    }

    @Test
    void testHasPermission_False() {
        Set<Permission> permissions = new HashSet<>(Arrays.asList(
                new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
        ));
        
        doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));

        boolean hasPermission = permissionService.hasPermission(1L, "/api/users/123", "POST");
        
        assertFalse(hasPermission);
    }

    @Test
    void testHasPermission_NullUserId() {
        boolean hasPermission = permissionService.hasPermission(null, "/api/users/123", "GET");
        
        assertFalse(hasPermission);
        verify(webClient, never()).get();
    }

    @Test
    void testGetPermissionPaths_Success() {
        Set<Permission> permissions = new HashSet<>(Arrays.asList(
                new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis()),
                new Permission(2L, "user:write", "Write User", "API", "/api/users/**", "POST", "Write user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
        ));
        
        doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));

        Set<String> paths = permissionService.getPermissionPaths(1L, "GET");
        
        assertNotNull(paths);
        assertEquals(1, paths.size());
        assertTrue(paths.contains("/api/users/**"));
    }

    @Test
    void testClearCache_Success() {
        User user = new User(1L, "testuser", "test@example.com", "1234567890", 1, System.currentTimeMillis(), System.currentTimeMillis());
        List<Role> roles = Arrays.asList(new Role(1L, "ADMIN", "Administrator", "Admin role", 1, System.currentTimeMillis(), System.currentTimeMillis()));
        Set<Permission> permissions = new HashSet<>(Arrays.asList(
                new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
        ));
        
        doReturn(Mono.just(user)).when(responseSpec).bodyToMono(eq(User.class));
        doReturn(Mono.just(roles.toArray(new Role[0]))).when(responseSpec).bodyToMono(eq(Role[].class));
        doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));

        permissionService.getUserById(1L);
        permissionService.getUserRoles(1L);
        permissionService.getUserPermissions(1L);
        
        permissionService.clearCache(1L);
        
        verify(webClient, times(3)).get();
    }

    @Test
    void testClearAllCache_Success() {
        User user = new User(1L, "testuser", "test@example.com", "1234567890", 1, System.currentTimeMillis(), System.currentTimeMillis());
        List<Role> roles = Arrays.asList(new Role(1L, "ADMIN", "Administrator", "Admin role", 1, System.currentTimeMillis(), System.currentTimeMillis()));
        Set<Permission> permissions = new HashSet<>(Arrays.asList(
                new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
        ));
        
        doReturn(Mono.just(user)).when(responseSpec).bodyToMono(eq(User.class));
        doReturn(Mono.just(roles.toArray(new Role[0]))).when(responseSpec).bodyToMono(eq(Role[].class));
        doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));

        permissionService.getUserById(1L);
        permissionService.getUserRoles(1L);
        permissionService.getUserPermissions(1L);
        
        permissionService.clearAllCache();
        
        permissionService.getUserById(1L);
        permissionService.getUserRoles(1L);
        permissionService.getUserPermissions(1L);
        
        verify(webClient, times(6)).get();
    }
}
