package com.mosquito.project.integration;

import com.mosquito.project.config.TestSecurityConfig;
import com.mosquito.project.controller.RewardController;
import com.mosquito.project.permission.ApprovalController;
import com.mosquito.project.permission.PermissionCheckService;
import com.mosquito.project.security.IntrospectionResponse;
import com.mosquito.project.security.UserIntrospectionService;
import com.mosquito.project.service.AuthService;
import com.mosquito.project.web.ApiKeyAuthInterceptor;
import com.mosquito.project.web.PermissionInterceptor;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import java.time.Instant;
import java.util.List;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.mockito.ArgumentMatchers.*;

/**
 * 权限执行集成测试
 * 验证权限拦截器正确返回403
 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ContextConfiguration(initializers = PermissionEnforcementIntegrationTest.Initializer.class)
@Import(TestSecurityConfig.class)
@DisplayName("权限执行集成测试 - 验证403拒绝场景")
class PermissionEnforcementIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserIntrospectionService userIntrospectionService;

    @MockBean
    private PermissionCheckService permissionCheckService;

    @MockBean
    private AuthService authService;

    @MockBean
    private ApiKeyAuthInterceptor apiKeyAuthInterceptor;

    @MockBean
    private PermissionInterceptor permissionInterceptor;

    static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        public void initialize(ConfigurableApplicationContext applicationContext) {
            TestPropertyValues.of(
                "spring.datasource.url=jdbc:h2:mem:testdb_permission;DB_CLOSE_DELAY=-1;MODE=PostgreSQL",
                "spring.datasource.username=sa",
                "spring.datasource.password=",
                "spring.datasource.driver-class-name=org.h2.Driver",
                "spring.jpa.hibernate.ddl-auto=create-drop",
                "spring.jpa.show-sql=false",
                "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect",
                "spring.liquibase.enabled=false",
                "spring.flyway.enabled=false",
                "logging.level.com.mosquito.project=WARN"
            ).applyTo(applicationContext.getEnvironment());
        }
    }

    private void setupNoPermissionMocks() {
        // 设置用户已登录但没有权限
        IntrospectionResponse active = new IntrospectionResponse();
        active.setActive(true);
        active.setUserId("no-permission-user");
        active.setTenantId("test-tenant");
        active.setRoles(List.of("USER"));
        active.setScopes(List.of("api"));
        long now = Instant.now().getEpochSecond();
        active.setIat(now);
        active.setExp(now + 3600);
        active.setJti("test-jti");
        Mockito.when(userIntrospectionService.introspect(anyString())).thenReturn(active);

        AuthService.TokenInfo tokenInfo = new AuthService.TokenInfo(99999L, Instant.now().plusSeconds(3600));
        Mockito.when(authService.validateToken(anyString())).thenReturn(tokenInfo);

        AuthService.UserInfo userInfo = new AuthService.UserInfo(
            99999L,
            "no-permission-user",
            null,
            "无权限用户",
            1L,
            List.of("USER"),
            List.of() // 没有权限
        );
        Mockito.when(authService.getUserById(anyLong())).thenReturn(userInfo);

        // 权限检查服务返回false（无权限）
        Mockito.when(permissionCheckService.hasPermission(anyLong(), anyString())).thenReturn(false);
        Mockito.when(permissionCheckService.hasRole(anyLong(), anyString())).thenReturn(false);
        Mockito.when(permissionCheckService.getUserDataScope(anyLong())).thenReturn("OWN");
    }

    @Test
    @DisplayName("审批接口 - 无权限应返回403")
    void approvalEndpoint_withoutPermission_shouldReturn403() throws Exception {
        setupNoPermissionMocks();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/approval/approve")
                .header("Authorization", "Bearer test-token")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"recordId\": 1, \"action\": \"APPROVE\", \"comment\": \"test\"}"))
                .andExpect(status().isForbidden())
                .andExpect(jsonPath("$.code").value(403))
                .andExpect(jsonPath("$.message").exists());
    }

    @Test
    @DisplayName("奖励接口 - 无权限应返回403")
    void rewardEndpoint_withoutPermission_shouldReturn403() throws Exception {
        setupNoPermissionMocks();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/rewards/admin/1/approve")
                .header("Authorization", "Bearer test-token")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"action\": \"approve\", \"comment\": \"test\"}"))
                .andExpect(status().isForbidden())
                .andExpect(jsonPath("$.code").value(403))
                .andExpect(jsonPath("$.message").exists());
    }

    @Test
    @DisplayName("奖励拒绝接口 - 无权限应返回403")
    void rewardRejectEndpoint_withoutPermission_shouldReturn403() throws Exception {
        setupNoPermissionMocks();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/rewards/admin/1/reject")
                .header("Authorization", "Bearer test-token")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"comment\": \"reject test\"}"))
                .andExpect(status().isForbidden())
                .andExpect(jsonPath("$.code").value(403))
                .andExpect(jsonPath("$.message").exists());
    }

    @Test
    @DisplayName("批量审批接口 - 无权限应返回403")
    void batchApprovalEndpoint_withoutPermission_shouldReturn403() throws Exception {
        setupNoPermissionMocks();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/approval/batch")
                .header("Authorization", "Bearer test-token")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"recordIds\": [1, 2], \"action\": \"APPROVE\", \"comment\": \"batch test\"}"))
                .andExpect(status().isForbidden())
                .andExpect(jsonPath("$.code").value(403))
                .andExpect(jsonPath("$.message").exists());
    }

    @Test
    @DisplayName("批量转交接口 - 无权限应返回403")
    void batchTransferEndpoint_withoutPermission_shouldReturn403() throws Exception {
        setupNoPermissionMocks();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/approval/batch-transfer")
                .header("Authorization", "Bearer test-token")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"recordIds\": [1, 2], \"transferTo\": 100, \"comment\": \"batch transfer test\"}"))
                .andExpect(status().isForbidden())
                .andExpect(jsonPath("$.code").value(403))
                .andExpect(jsonPath("$.message").exists());
    }

    @Test
    @DisplayName("委托审批接口 - 无权限应返回403")
    void delegateEndpoint_withoutPermission_shouldReturn403() throws Exception {
        setupNoPermissionMocks();

        mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/approval/delegate")
                .header("Authorization", "Bearer test-token")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"recordId\": 1, \"delegateTo\": 100, \"reason\": \"delegate test\"}"))
                .andExpect(status().isForbidden())
                .andExpect(jsonPath("$.code").value(403))
                .andExpect(jsonPath("$.message").exists());
    }

    @Test
    @DisplayName("未登录访问受保护接口应返回401")
    void withoutLogin_shouldReturn401() throws Exception {
        // 不设置任何认证信息

        mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/approval/approve")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"recordId\": 1, \"action\": \"APPROVE\", \"comment\": \"test\"}"))
                .andExpect(status().isUnauthorized());
    }
}