package com.ga.api.controller.system;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ga.api.service.system.UserService;
import com.ga.api.support.AbstractControllerTest;
import com.ga.api.support.MockLoginUser;
import com.ga.common.menu.PermissionChecker;
import com.ga.common.model.PageResponse;
import com.ga.core.vo.user.UserResp;
import com.ga.core.vo.user.UserSaveReq;
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 UserControllerTest extends AbstractControllerTest {

    @Autowired ObjectMapper objectMapper;

    @MockBean UserService service;
    @MockBean PermissionChecker permissionChecker;

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

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

    private UserResp sampleUser() {
        UserResp r = new UserResp();
        r.setUserId(99L);
        r.setLoginId("admin");
        r.setUserName("관리자");
        r.setStatus("ACTIVE");
        return r;
    }

    private UserSaveReq validReq() {
        UserSaveReq req = new UserSaveReq();
        req.setLoginId("newuser");
        req.setUserName("신규사용자");
        req.setEmail("new@example.com");
        return req;
    }

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

    @Test
    @DisplayName("GET /api/system/users - 200 사용자 목록")
    void list_ok() throws Exception {
        PageResponse<UserResp> page = PageResponse.<UserResp>builder()
                .list(List.of(sampleUser()))
                .pageNum(1).pageSize(10).total(1).pages(1)
                .build();
        given(service.list(any())).willReturn(page);

        mockMvc.perform(get("/api/system/users").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true))
                .andExpect(jsonPath("$.data.list[0].loginId").value("admin"));
    }

    @Test
    @DisplayName("GET /api/system/users/{id} - 200 사용자 상세")
    void detail_ok() throws Exception {
        given(service.detail(99L)).willReturn(sampleUser());

        mockMvc.perform(get("/api/system/users/99").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.userId").value(99));
    }

    @Test
    @DisplayName("POST /api/system/users - 200 사용자 등록 성공")
    void create_ok() throws Exception {
        given(service.create(any())).willReturn(99L);

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

    @Test
    @DisplayName("POST /api/system/users - 400 loginId 누락")
    void create_missingLoginId() throws Exception {
        UserSaveReq req = new UserSaveReq();
        req.setUserName("신규사용자");
        // loginId 없음

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

    @Test
    @DisplayName("PUT /api/system/users/{id}/reset-password - 200 비밀번호 초기화")
    void resetPassword_ok() throws Exception {
        willDoNothing().given(service).resetPassword(99L);

        mockMvc.perform(put("/api/system/users/99/reset-password").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true));
    }

    @Test
    @DisplayName("PUT /api/system/users/{id}/unlock - 200 계정 잠금 해제")
    void unlock_ok() throws Exception {
        willDoNothing().given(service).unlock(99L);

        mockMvc.perform(put("/api/system/users/99/unlock").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true));
    }

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

    @Test
    @DisplayName("POST /api/system/users - 403 권한 없는 요청")
    void create_forbidden() throws Exception {
        given(permissionChecker.hasPermission(anyLong(), eq("SYSTEM_USER"), eq("CREATE"))).willReturn(false);

        mockMvc.perform(post("/api/system/users")
                        .with(MockLoginUser.admin())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(validReq())))
                .andExpect(status().isForbidden());
    }
}
