package com.ga.api.controller.org;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ga.api.service.org.AgentService;
import com.ga.api.support.AbstractControllerTest;
import com.ga.api.support.MockLoginUser;
import com.ga.common.excel.ExcelService;
import com.ga.common.menu.PermissionChecker;
import com.ga.common.model.PageResponse;
import com.ga.core.vo.org.AgentResp;
import com.ga.core.vo.org.AgentSaveReq;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;

import java.util.List;

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

class AgentControllerTest extends AbstractControllerTest {

    @Autowired ObjectMapper objectMapper;

    @MockBean AgentService agentService;
    @MockBean ExcelService excelService;
    @MockBean PermissionChecker permissionChecker;

    @BeforeEach
    void permitAll() {
        given(permissionChecker.hasPermission(anyLong(), anyString(), anyString())).willReturn(true);
    }

    // ---- 픽스처 ----

    private AgentResp sampleAgent() {
        AgentResp r = new AgentResp();
        r.setAgentId(1L);
        r.setAgentName("홍길동");
        r.setStatus("ACTIVE");
        return r;
    }

    private AgentSaveReq validReq() {
        AgentSaveReq req = new AgentSaveReq();
        req.setAgentName("홍길동");
        req.setOrgId(1L);
        req.setGradeId(1L);
        return req;
    }

    // ---- 테스트 ----

    @Test
    @DisplayName("GET /api/agents - 200 목록 반환")
    void list_ok() throws Exception {
        PageResponse<AgentResp> page = PageResponse.<AgentResp>builder()
                .list(List.of(sampleAgent()))
                .pageNum(1).pageSize(10).total(1).pages(1)
                .build();
        given(agentService.list(any())).willReturn(page);

        mockMvc.perform(get("/api/agents").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true))
                .andExpect(jsonPath("$.data.total").value(1))
                .andExpect(jsonPath("$.data.list[0].agentName").value("홍길동"));
    }

    @Test
    @DisplayName("GET /api/agents/{id} - 200 상세 반환")
    void detail_ok() throws Exception {
        given(agentService.detail(1L)).willReturn(sampleAgent());

        mockMvc.perform(get("/api/agents/1").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.agentId").value(1));
    }

    @Test
    @DisplayName("POST /api/agents - 200 등록 성공")
    void create_ok() throws Exception {
        given(agentService.create(any())).willReturn(1L);

        mockMvc.perform(post("/api/agents")
                        .with(MockLoginUser.admin())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(validReq())))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true))
                .andExpect(jsonPath("$.data").value(1));
    }

    @Test
    @DisplayName("POST /api/agents - 400 필수값 누락 (agentName null)")
    void create_validationFail() throws Exception {
        AgentSaveReq req = new AgentSaveReq();
        req.setOrgId(1L);
        req.setGradeId(1L);
        // agentName 없음

        mockMvc.perform(post("/api/agents")
                        .with(MockLoginUser.admin())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(req)))
                .andExpect(status().isBadRequest());
    }

    @Test
    @DisplayName("PUT /api/agents/{id} - 200 수정 성공")
    void update_ok() throws Exception {
        willDoNothing().given(agentService).update(eq(1L), any());

        mockMvc.perform(put("/api/agents/1")
                        .with(MockLoginUser.admin())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(validReq())))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true));
    }

    @Test
    @DisplayName("GET /api/agents - 401 인증 없는 요청")
    void list_unauthenticated() throws Exception {
        mockMvc.perform(get("/api/agents"))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @DisplayName("GET /api/agents - 403 권한 없는 요청")
    void list_forbidden() throws Exception {
        given(permissionChecker.hasPermission(anyLong(), eq("ORG_AGENT"), eq("READ"))).willReturn(false);

        mockMvc.perform(get("/api/agents").with(MockLoginUser.admin()))
                .andExpect(status().isForbidden());
    }
}
