package com.ga.api.controller.settle;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ga.api.service.settle.SettleService;
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.settle.SettleMasterResp;
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.math.BigDecimal;
import java.util.List;
import java.util.Map;

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 SettleControllerTest extends AbstractControllerTest {

    @Autowired ObjectMapper objectMapper;

    @MockBean SettleService service;
    @MockBean PermissionChecker permissionChecker;

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

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

    private SettleMasterResp sampleSettle() {
        SettleMasterResp r = new SettleMasterResp();
        r.setSettleId(50L);
        r.setAgentId(1L);
        r.setAgentName("홍길동");
        r.setSettleMonth("202401");
        r.setStatus("CALCULATED");
        r.setNetAmount(new BigDecimal("300000"));
        return r;
    }

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

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

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

    @Test
    @DisplayName("GET /api/settle/summary - 200 월별 요약")
    void summary_ok() throws Exception {
        given(service.summary("202401")).willReturn(
                Map.of("totalAmount", 1000000, "agentCount", 5));

        mockMvc.perform(get("/api/settle/summary")
                        .param("settleMonth", "202401")
                        .with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.agentCount").value(5));
    }

    @Test
    @DisplayName("GET /api/settle/{agentId}/months/{settleMonth} - 200 상세")
    void detail_ok() throws Exception {
        given(service.detail(1L, "202401")).willReturn(sampleSettle());

        mockMvc.perform(get("/api/settle/1/months/202401").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.settleId").value(50));
    }

    @Test
    @DisplayName("PUT /api/settle/{id}/confirm - 200 확정 처리")
    void confirm_ok() throws Exception {
        willDoNothing().given(service).confirm(50L);

        mockMvc.perform(put("/api/settle/50/confirm").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true));
    }

    @Test
    @DisplayName("PUT /api/settle/{id}/hold - 200 보류 처리")
    void hold_ok() throws Exception {
        willDoNothing().given(service).hold(eq(50L), anyString(), any());

        mockMvc.perform(put("/api/settle/50/hold")
                        .with(MockLoginUser.admin())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(Map.of("reason", "검토 필요"))))
                .andExpect(status().isOk());
    }

    @Test
    @DisplayName("PUT /api/settle/{id}/confirm - 403 APPROVE 권한 없음")
    void confirm_forbidden() throws Exception {
        given(permissionChecker.hasPermission(anyLong(), eq("SETTLE_LIST"), eq("APPROVE"))).willReturn(false);

        mockMvc.perform(put("/api/settle/50/confirm").with(MockLoginUser.admin()))
                .andExpect(status().isForbidden());
    }

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