package com.cf.imes.module.system.controller.admin.funds.invoice;

import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.*;
import com.cf.imes.module.system.enums.pay.InvoiceIssueTypeEnum;
import com.cf.imes.module.system.enums.pay.InvoiceTypeEnum;
import com.cf.imes.module.system.service.funds.invoice.InvoiceService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import java.math.BigDecimal;
import java.util.List;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * @author 发票管理 Controller 测试类
 */
@WebMvcTest(controllers = InvoiceController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
class InvoiceControllerTest extends BaseWebUnitTest {
    @Autowired
    private MockMvc mockMvc;

    @MockitoBean
    private InvoiceService invoiceService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void testGetOrgInvoiceTitle() throws Exception {
        InvoiceTitleInfo respVO = new InvoiceTitleInfo();
        respVO.setId(1L);
        respVO.setInvoiceTitle("测试公司");

        when(invoiceService.getInvoiceTitle()).thenReturn(respVO);

        mockMvc.perform(get("/system/invoice/title"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.id").value(1L))
                .andExpect(jsonPath("$.data.invoiceTitle").value("测试公司"));

        verify(invoiceService).getInvoiceTitle();
    }

    @Test
    void testInsetInvoiceTitle() throws Exception {
        InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO();
        reqVO.setInvoiceTitle("测试公司");
        reqVO.setIssueType(InvoiceIssueTypeEnum.ENTERPRISE.getValue());
        reqVO.setInvoiceType(InvoiceTypeEnum.VAT_GENERAL.getValue());
        reqVO.setEmail("test@test.com");

        doNothing().when(invoiceService).insetInvoiceTitle(any());

        mockMvc.perform(post("/system/invoice/title")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));

        verify(invoiceService).insetInvoiceTitle(any());
    }

    @Test
    void testGetInvoiceAmount() throws Exception {
        InvoiceAmountRespVO respVO = new InvoiceAmountRespVO();
        respVO.setInvocable(new BigDecimal("100.00"));
        respVO.setPendingInvoice(new BigDecimal("50.00"));
        respVO.setInTheInvoice(new BigDecimal("30.00"));
        respVO.setInvoiced(new BigDecimal("200.00"));

        when(invoiceService.getInvoiceAmount()).thenReturn(respVO);

        mockMvc.perform(get("/system/invoice/org/amount"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.invocable").value(100.00))
                .andExpect(jsonPath("$.data.pendingInvoice").value(50.00))
                .andExpect(jsonPath("$.data.inTheInvoice").value(30.00))
                .andExpect(jsonPath("$.data.invoiced").value(200.00));

        verify(invoiceService).getInvoiceAmount();
    }

    @Test
    void testGetManageEndPointInvoiceAmount() throws Exception {
        InvoiceAmountRespVO respVO = new InvoiceAmountRespVO();
        respVO.setPendingInvoice(new BigDecimal("150.00"));
        respVO.setInvoiced(new BigDecimal("300.00"));

        when(invoiceService.getManageInvoiceAmount()).thenReturn(respVO);

        mockMvc.perform(get("/system/invoice/manage/amount"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.pendingInvoice").value(150.00))
                .andExpect(jsonPath("$.data.invoiced").value(300.00));

        verify(invoiceService).getManageInvoiceAmount();
    }

    @Test
    void testGetAvailablePage() throws Exception {
        InvoiceAmountAndAvailablePageReqVO reqVO = new InvoiceAmountAndAvailablePageReqVO();
        PageResult<InvoiceAvailablePageRespVO> mockPage = new PageResult<>();
        mockPage.setTotal(1L);
        InvoiceAvailablePageRespVO pageRespVO = new InvoiceAvailablePageRespVO();
        pageRespVO.setId(1L);
        pageRespVO.setAvailableAmount(new BigDecimal("100.00"));
        mockPage.setList(List.of(pageRespVO));
        
        when(invoiceService.getAvailablePage(any())).thenReturn(mockPage);

        mockMvc.perform(get("/system/invoice/available")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.list[0].id").value(1L))
                .andExpect(jsonPath("$.data.list[0].availableAmount").value(100.00));

        verify(invoiceService, times(1)).getAvailablePage(any());
    }

    @Test
    void testApplyInvoiceRecords() throws Exception {
        InvoiceRecordsReqVO reqVO = new InvoiceRecordsReqVO();
        reqVO.setPurchaseRecordId(List.of(1L, 2L));

        doNothing().when(invoiceService).applyInvoice(any());

        mockMvc.perform(post("/system/invoice")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));

        verify(invoiceService).applyInvoice(any());
    }

    @Test
    void testGetInvoiceRecords() throws Exception {
        InvoiceDetailsPageReqVO reqVO = new InvoiceDetailsPageReqVO();
        PageResult<InvoiceRecordsRespVO> mockPage = new PageResult<>();
        mockPage.setTotal(1L);
        InvoiceRecordsRespVO pageRespVO = new InvoiceRecordsRespVO();
        pageRespVO.setId(1L);
        pageRespVO.setInvoiceAmount(new BigDecimal("150.00"));
        mockPage.setList(List.of(pageRespVO));
        
        when(invoiceService.getInvoiceRecords(any())).thenReturn(mockPage);

        mockMvc.perform(get("/system/invoice/records")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.list[0].id").value(1L))
                .andExpect(jsonPath("$.data.list[0].invoiceAmount").value(150.00));

        verify(invoiceService, times(1)).getInvoiceRecords(any());
    }

    @Test
    void testGetInvoiceRecordDetail() throws Exception {
        Long recordId = 1L;
        InvoiceDetailsRespVO respVO = new InvoiceDetailsRespVO();
        respVO.setInvoiceNo("FP20231010101010");
        respVO.setRemark("测试备注");

        when(invoiceService.getInvoiceRecordDetail(recordId)).thenReturn(respVO);

        mockMvc.perform(get("/system/invoice/record/{recordId}", recordId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.invoiceNo").value("FP20231010101010"))
                .andExpect(jsonPath("$.data.remark").value("测试备注"));

        verify(invoiceService).getInvoiceRecordDetail(recordId);
    }

    @Test
    void testConfirmInvoice() throws Exception {
        InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO();
        reqVO.setId(1L);
        reqVO.setAgree(true);
        reqVO.setInvoiceNo("FP20231010101010");
        reqVO.setRemark("同意开票");

        doNothing().when(invoiceService).confirmInvoice(any(), any());

        MockMultipartFile file = new MockMultipartFile(
                "file",
                "invoice.pdf",
                "application/pdf",
                "test data".getBytes()
        );

        // 使用param方式传递reqVO参数
        mockMvc.perform(multipart("/system/invoice/confirm")
                        .file(file)
                        .param("id", "1")
                        .param("agree", "true")
                        .param("invoiceNo", "FP20231010101010")
                        .param("remark", "同意开票"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));

        verify(invoiceService).confirmInvoice(any(), any());
    }
}