package com.ga.api.controller.ledger;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ga.api.service.ledger.LedgerService;
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.ledger.ExceptionLedgerResp;
import com.ga.core.vo.ledger.ExceptionLedgerSaveReq;
import com.ga.core.vo.ledger.LedgerResp;
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.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

class LedgerControllerTest extends AbstractControllerTest {

    @Autowired ObjectMapper objectMapper;

    @MockBean LedgerService service;
    @MockBean ExcelService excelService;
    @MockBean PermissionChecker permissionChecker;

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

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

    private PageResponse<LedgerResp> singlePage() {
        LedgerResp r = new LedgerResp();
        r.setLedgerId(100L);
        r.setAgentName("홍길동");
        r.setPolicyNo("POL-001");
        return PageResponse.<LedgerResp>builder()
                .list(List.of(r))
                .pageNum(1).pageSize(10).total(1).pages(1)
                .build();
    }

    private ExceptionLedgerSaveReq validExceptionReq() {
        ExceptionLedgerSaveReq req = new ExceptionLedgerSaveReq();
        req.setAgentId(1L);
        req.setExceptionCode("EXTRA_PAY");
        req.setSettleMonth("202401");
        req.setAmount(new BigDecimal("50000"));
        return req;
    }

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

    @Test
    @DisplayName("GET /api/ledger/recruit - 200 모집 원장 목록")
    void listRecruit_ok() throws Exception {
        given(service.listRecruit(any())).willReturn(singlePage());

        mockMvc.perform(get("/api/ledger/recruit").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true))
                .andExpect(jsonPath("$.data.list[0].policyNo").value("POL-001"));
    }

    @Test
    @DisplayName("GET /api/ledger/maintain - 200 유지 원장 목록")
    void listMaintain_ok() throws Exception {
        given(service.listMaintain(any())).willReturn(singlePage());

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

    @Test
    @DisplayName("GET /api/ledger/exception - 200 예외 원장 목록")
    void listException_ok() throws Exception {
        ExceptionLedgerResp er = new ExceptionLedgerResp();
        er.setLedgerId(200L);
        er.setExceptionCode("EXTRA_PAY");
        PageResponse<ExceptionLedgerResp> page = PageResponse.<ExceptionLedgerResp>builder()
                .list(List.of(er))
                .pageNum(1).pageSize(10).total(1).pages(1)
                .build();
        given(service.listException(any())).willReturn(page);

        mockMvc.perform(get("/api/ledger/exception").with(MockLoginUser.admin()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.list[0].exceptionCode").value("EXTRA_PAY"));
    }

    @Test
    @DisplayName("POST /api/ledger/exception - 200 예외 원장 등록")
    void createException_ok() throws Exception {
        given(service.createException(any())).willReturn(200L);

        mockMvc.perform(post("/api/ledger/exception")
                        .with(MockLoginUser.admin())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(validExceptionReq())))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(200));
    }

    @Test
    @DisplayName("POST /api/ledger/exception - 400 필수값 누락 (agentId null)")
    void createException_validationFail() throws Exception {
        ExceptionLedgerSaveReq req = new ExceptionLedgerSaveReq();
        req.setExceptionCode("EXTRA_PAY");
        req.setSettleMonth("202401");
        req.setAmount(new BigDecimal("50000"));
        // agentId 없음

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

    @Test
    @DisplayName("PUT /api/ledger/exception/{id}/approve - 200 승인 처리")
    void approveException_ok() throws Exception {
        mockMvc.perform(put("/api/ledger/exception/200/approve")
                        .with(MockLoginUser.admin())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(Map.of("status", "APPROVED"))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true));
    }

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