package com.linearpast.minecraftmanager.controller;

import com.linearpast.minecraftmanager.entity.Operators;
import com.linearpast.minecraftmanager.entity.Players;
import com.linearpast.minecraftmanager.entity.view.PlayerInfoView;
import com.linearpast.minecraftmanager.service.inter.PlayersService;
import com.linearpast.minecraftmanager.utils.Result;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import java.util.Collections;
import java.util.List;

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

@ExtendWith(MockitoExtension.class)
class WhitelistControllerTest {

    @Mock
    private PlayersService playersService;

    @InjectMocks
    private WhitelistController controller;

    private MockMvc mockMvc;
    private MockHttpSession adminSession;

    @BeforeEach
    void setUp() {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();

        Operators admin = new Operators();
        admin.setId(1);
        admin.setUsername("testAdmin");

        adminSession = new MockHttpSession();
        adminSession.setAttribute("adminAccount", admin);
        adminSession.setAttribute("isLoggedIn", true);
    }

    // ==================== list ====================

    @Test
    void getWhitelist_shouldReturnPage() throws Exception {
        Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
        when(playersService.getPlayers(isNull(), isNull(), isNull(), eq((byte) 1),
                isNull(), isNull(), any(Pageable.class))).thenReturn(page);

        mockMvc.perform(get("/api/whitelist/list")
                        .param("page", "1").param("size", "10"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.count").value(0));
    }

    @Test
    void getWhitelist_withPlayerName_shouldFilter() throws Exception {
        Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
        when(playersService.getPlayers(eq("testPlayer"), isNull(), isNull(), eq((byte) 1),
                isNull(), isNull(), any(Pageable.class))).thenReturn(page);

        mockMvc.perform(get("/api/whitelist/list").param("playerName", "testPlayer"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    // ==================== pending ====================

    @Test
    void getPending_shouldReturnPage() throws Exception {
        Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
        when(playersService.getPlayers(isNull(), isNull(), isNull(), eq((byte) 2),
                isNull(), isNull(), any(Pageable.class))).thenReturn(page);

        mockMvc.perform(get("/api/whitelist/pending"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    // ==================== rejected ====================

    @Test
    void getRejected_shouldReturnPage() throws Exception {
        Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
        when(playersService.getPlayers(isNull(), isNull(), isNull(), eq((byte) 0),
                isNull(), isNull(), any(Pageable.class))).thenReturn(page);

        mockMvc.perform(get("/api/whitelist/rejected"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    // ==================== approve ====================

    @Test
    void approve_success() throws Exception {
        when(playersService.updatePlayerStatus(eq(1), eq((byte) 1), any(Operators.class))).thenReturn(1);

        mockMvc.perform(post("/api/whitelist/approve/1").session(adminSession))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.msg").value("已加入白名单"));
    }

    @Test
    void approve_failure() throws Exception {
        when(playersService.updatePlayerStatus(eq(99), eq((byte) 1), any(Operators.class))).thenReturn(0);

        mockMvc.perform(post("/api/whitelist/approve/99").session(adminSession))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500));
    }

    @Test
    void approve_withoutSession_passNullOperator() throws Exception {
        when(playersService.updatePlayerStatus(eq(1), eq((byte) 1), isNull())).thenReturn(1);

        mockMvc.perform(post("/api/whitelist/approve/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    // ==================== reject ====================

    @Test
    void reject_success() throws Exception {
        when(playersService.updatePlayerStatus(eq(1), eq((byte) 0), any(Operators.class))).thenReturn(1);

        mockMvc.perform(post("/api/whitelist/reject/1").session(adminSession))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.msg").value("已拒绝申请"));
    }

    @Test
    void reject_failure() throws Exception {
        when(playersService.updatePlayerStatus(eq(99), eq((byte) 0), any(Operators.class))).thenReturn(0);

        mockMvc.perform(post("/api/whitelist/reject/99").session(adminSession))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500));
    }

    // ==================== remove ====================

    @Test
    void removeFromWhitelist_success() throws Exception {
        when(playersService.deletePlayer(1)).thenReturn(true);

        mockMvc.perform(delete("/api/whitelist/remove/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.msg").value("已从白名单移除"));
    }

    @Test
    void removeFromWhitelist_failure() throws Exception {
        when(playersService.deletePlayer(99)).thenReturn(false);

        mockMvc.perform(delete("/api/whitelist/remove/99"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500));
    }

    // ==================== batchApprove ====================

    @Test
    void batchApprove_success() throws Exception {
        List<Integer> ids = List.of(1, 2, 3);
        when(playersService.updatePlayersStatus(eq(ids), eq((byte) 1), any(Operators.class))).thenReturn(3);

        mockMvc.perform(post("/api/whitelist/batchApprove")
                        .session(adminSession)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("[1,2,3]"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.msg").value("成功处理：3/3"));
    }

    @Test
    void batchApprove_partialSuccess() throws Exception {
        List<Integer> ids = List.of(1, 2, 3);
        when(playersService.updatePlayersStatus(eq(ids), eq((byte) 1), any(Operators.class))).thenReturn(2);

        mockMvc.perform(post("/api/whitelist/batchApprove")
                        .session(adminSession)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("[1,2,3]"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.msg").value("成功处理：2/3"));
    }

    @Test
    void batchApprove_allFailed() throws Exception {
        List<Integer> ids = List.of(1, 2);
        when(playersService.updatePlayersStatus(eq(ids), eq((byte) 1), any(Operators.class))).thenReturn(0);

        mockMvc.perform(post("/api/whitelist/batchApprove")
                        .session(adminSession)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("[1,2]"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500));
    }

    // ==================== batchReject ====================

    @Test
    void batchReject_success() throws Exception {
        List<Integer> ids = List.of(1, 2);
        when(playersService.updatePlayersStatus(eq(ids), eq((byte) 0), any(Operators.class))).thenReturn(2);

        mockMvc.perform(post("/api/whitelist/batchReject")
                        .session(adminSession)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("[1,2]"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    // ==================== batchRemove ====================

    @Test
    void batchRemove_success() throws Exception {
        List<Integer> ids = List.of(1, 2, 3);
        when(playersService.deletePlayers(ids)).thenReturn(3);

        mockMvc.perform(delete("/api/whitelist/batchRemove")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("[1,2,3]"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.msg").value("成功移除：3/3"));
    }

    @Test
    void batchRemove_allFailed() throws Exception {
        List<Integer> ids = List.of(1, 2);
        when(playersService.deletePlayers(ids)).thenReturn(0);

        mockMvc.perform(delete("/api/whitelist/batchRemove")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("[1,2]"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500));
    }

    // ==================== stats ====================

    @Test
    void getStats_shouldReturnCounts() throws Exception {
        when(playersService.getPlayersCountByStatus((byte) 1)).thenReturn(10);
        when(playersService.getPlayersCountByStatus((byte) 2)).thenReturn(5);
        when(playersService.getPlayersCountByStatus((byte) 0)).thenReturn(3);

        mockMvc.perform(get("/api/whitelist/stats"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.approved").value(10))
                .andExpect(jsonPath("$.data.pending").value(5))
                .andExpect(jsonPath("$.data.rejected").value(3));
    }

    // ==================== check ====================

    @Test
    void checkPlayer_exists() throws Exception {
        Players player = new Players();
        player.setPlayerName("Steve");
        player.setQq("12345");
        player.setUuid("uuid-123");
        player.setStatus((byte) 1);

        when(playersService.getPlayer("Steve")).thenReturn(player);

        mockMvc.perform(get("/api/whitelist/check/Steve"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.exists").value(true))
                .andExpect(jsonPath("$.data.status").value(1))
                .andExpect(jsonPath("$.data.statusText").value("已通过"));
    }

    @Test
    void checkPlayer_notExists() throws Exception {
        when(playersService.getPlayer("Notch")).thenReturn(null);

        mockMvc.perform(get("/api/whitelist/check/Notch"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.exists").value(false));
    }

    @Test
    void checkPlayer_pendingStatus() throws Exception {
        Players player = new Players();
        player.setStatus((byte) 2);

        when(playersService.getPlayer("Newbie")).thenReturn(player);

        mockMvc.perform(get("/api/whitelist/check/Newbie"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.statusText").value("待审核"));
    }

    @Test
    void checkPlayer_rejectedStatus() throws Exception {
        Players player = new Players();
        player.setStatus((byte) 0);

        when(playersService.getPlayer("RejectedGuy")).thenReturn(player);

        mockMvc.perform(get("/api/whitelist/check/RejectedGuy"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.statusText").value("已拒绝"));
    }

    // ==================== score ====================

    @Test
    void getPlayerScore_success() throws Exception {
        when(playersService.getPlayerScoreById(1)).thenReturn(85);

        mockMvc.perform(get("/api/whitelist/score/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.playerId").value(1))
                .andExpect(jsonPath("$.data.totalScore").value(85));
    }

    @Test
    void getPlayerScore_notFound() throws Exception {
        when(playersService.getPlayerScoreById(999)).thenReturn(null);

        mockMvc.perform(get("/api/whitelist/score/999"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500))
                .andExpect(jsonPath("$.msg").value("玩家不存在"));
    }

    // ==================== session: null operator for API key calls ====================

    @Test
    void approve_apiKeyCall_shouldPassNullOperator() throws Exception {
        when(playersService.updatePlayerStatus(eq(1), eq((byte) 1), isNull())).thenReturn(1);

        mockMvc.perform(post("/api/whitelist/approve/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }
}
