package com.ruoyi.ccdi.project.controller;

import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectAbnormalAccountPageVO;
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativeItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiProjectOverviewControllerTest {

    @InjectMocks
    private CcdiProjectOverviewController controller;

    @Mock
    private ICcdiProjectOverviewService overviewService;

    @Mock
    private CcdiProjectAccessService projectAccessService;

    @Test
    void shouldExposeDashboardEndpoint() throws Exception {
        when(overviewService.getDashboard(40L)).thenReturn(new CcdiProjectOverviewDashboardVO());

        AjaxResult result = controller.getDashboard(40L);

        assertEquals(200, result.get("code"));
        verify(overviewService).getDashboard(40L);

        RequestMapping mapping = CcdiProjectOverviewController.class.getAnnotation(RequestMapping.class);
        Method method = CcdiProjectOverviewController.class.getMethod("getDashboard", Long.class);
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
        Operation operation = method.getAnnotation(Operation.class);

        assertNotNull(mapping);
        assertEquals("/ccdi/project/overview", mapping.value()[0]);
        assertNotNull(getMapping);
        assertEquals("/dashboard", getMapping.value()[0]);
        assertNotNull(preAuthorize);
        assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
        assertNotNull(operation);
    }

    @Test
    void shouldExposeRiskPeopleEndpoint() throws Exception {
        CcdiProjectRiskPeopleOverviewItemVO item = new CcdiProjectRiskPeopleOverviewItemVO();
        item.setRiskLevel("中风险");
        item.setRiskLevelType("warning");
        item.setModelCount(4);
        CcdiProjectRiskHitTagVO riskPointTag = new CcdiProjectRiskHitTagVO();
        riskPointTag.setModelCode("SALARY");
        riskPointTag.setModelName("产薪异常模型");
        riskPointTag.setRuleName("多工资转入");
        item.setRiskPointTagList(List.of(riskPointTag));
        CcdiProjectRiskPeopleOverviewVO overview = new CcdiProjectRiskPeopleOverviewVO();
        Method setRowsMethod = CcdiProjectRiskPeopleOverviewVO.class.getMethod("setRows", List.class);
        Method setTotalMethod = CcdiProjectRiskPeopleOverviewVO.class.getMethod("setTotal", Long.class);
        Method setPageNumMethod = CcdiProjectRiskPeopleOverviewVO.class.getMethod("setPageNum", Long.class);
        Method setPageSizeMethod = CcdiProjectRiskPeopleOverviewVO.class.getMethod("setPageSize", Long.class);
        Method getRowsMethod = CcdiProjectRiskPeopleOverviewVO.class.getMethod("getRows");
        Method getTotalMethod = CcdiProjectRiskPeopleOverviewVO.class.getMethod("getTotal");
        Method getPageNumMethod = CcdiProjectRiskPeopleOverviewVO.class.getMethod("getPageNum");
        Method getPageSizeMethod = CcdiProjectRiskPeopleOverviewVO.class.getMethod("getPageSize");
        setRowsMethod.invoke(overview, List.of(item));
        setTotalMethod.invoke(overview, 1L);
        setPageNumMethod.invoke(overview, 1L);
        setPageSizeMethod.invoke(overview, 5L);

        assertEquals("中风险", ((List<CcdiProjectRiskPeopleOverviewItemVO>) getRowsMethod.invoke(overview)).getFirst().getRiskLevel());
        assertEquals("warning", ((List<CcdiProjectRiskPeopleOverviewItemVO>) getRowsMethod.invoke(overview)).getFirst().getRiskLevelType());
        assertEquals(4, ((List<CcdiProjectRiskPeopleOverviewItemVO>) getRowsMethod.invoke(overview)).getFirst().getModelCount());
        assertEquals("SALARY", ((List<CcdiProjectRiskPeopleOverviewItemVO>) getRowsMethod.invoke(overview)).getFirst().getRiskPointTagList().getFirst().getModelCode());
        assertEquals(1L, getTotalMethod.invoke(overview));
        assertEquals(1L, getPageNumMethod.invoke(overview));
        assertEquals(5L, getPageSizeMethod.invoke(overview));

        Method method = CcdiProjectOverviewController.class.getMethod(
            "getRiskPeople",
            Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO")
        );
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);

        assertNotNull(getMapping);
        assertEquals("/risk-people", getMapping.value()[0]);
        assertNotNull(preAuthorize);
        assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
    }

    @Test
    void shouldExposeTopRiskPeopleEndpoint() throws Exception {
        when(overviewService.getTopRiskPeople(40L)).thenReturn(new CcdiProjectTopRiskPeopleVO());

        AjaxResult result = controller.getTopRiskPeople(40L);

        assertEquals(200, result.get("code"));
        verify(overviewService).getTopRiskPeople(40L);

        Method method = CcdiProjectOverviewController.class.getMethod("getTopRiskPeople", Long.class);
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);

        assertNotNull(getMapping);
        assertEquals("/top-risk-people", getMapping.value()[0]);
        assertNotNull(preAuthorize);
        assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
    }

    @Test
    void shouldExposePersonAnalysisDetailEndpoint() throws Exception {
        CcdiProjectPersonAnalysisDetailQueryDTO queryDTO = new CcdiProjectPersonAnalysisDetailQueryDTO();
        queryDTO.setProjectId(40L);
        queryDTO.setStaffIdCard("330000000000000001");

        CcdiProjectPersonAnalysisBasicInfoVO basicInfo = new CcdiProjectPersonAnalysisBasicInfoVO();
        basicInfo.setName("李四");
        CcdiProjectPersonAnalysisDetailVO detail = new CcdiProjectPersonAnalysisDetailVO();
        detail.setBasicInfo(basicInfo);
        when(overviewService.getPersonAnalysisDetail(queryDTO)).thenReturn(detail);

        AjaxResult result = controller.getPersonAnalysisDetail(queryDTO);

        assertEquals(200, result.get("code"));
        CcdiProjectPersonAnalysisDetailVO data = (CcdiProjectPersonAnalysisDetailVO) result.get("data");
        assertEquals("李四", data.getBasicInfo().getName());
        verify(overviewService).getPersonAnalysisDetail(queryDTO);

        Method method = CcdiProjectOverviewController.class.getMethod(
            "getPersonAnalysisDetail",
            CcdiProjectPersonAnalysisDetailQueryDTO.class
        );
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
        Operation operation = method.getAnnotation(Operation.class);

        assertNotNull(getMapping);
        assertEquals("/person-analysis/detail", getMapping.value()[0]);
        assertNotNull(preAuthorize);
        assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
        assertNotNull(operation);
    }

    @Test
    void shouldExposeSuspiciousTransactionsEndpoint() throws Exception {
        CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
        queryDTO.setProjectId(40L);
        queryDTO.setSuspiciousType("ALL");
        queryDTO.setPageNum(1);
        queryDTO.setPageSize(10);

        CcdiProjectSuspiciousTransactionPageVO pageVO = new CcdiProjectSuspiciousTransactionPageVO();
        pageVO.setRows(List.of());
        pageVO.setTotal(0L);
        when(overviewService.getSuspiciousTransactions(queryDTO)).thenReturn(pageVO);

        AjaxResult result = controller.getSuspiciousTransactions(queryDTO);

        assertEquals(200, result.get("code"));
        assertEquals(pageVO, result.get("data"));
        verify(overviewService).getSuspiciousTransactions(queryDTO);

        Method method = CcdiProjectOverviewController.class.getMethod(
            "getSuspiciousTransactions",
            CcdiProjectSuspiciousTransactionQueryDTO.class
        );
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);

        assertNotNull(getMapping);
        assertEquals("/suspicious-transactions", getMapping.value()[0]);
        assertNotNull(preAuthorize);
        assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
    }

    @Test
    void shouldExposeEmployeeCreditNegativeEndpoint() throws Exception {
        CcdiProjectEmployeeCreditNegativeQueryDTO queryDTO = new CcdiProjectEmployeeCreditNegativeQueryDTO();
        queryDTO.setProjectId(40L);
        queryDTO.setPageNum(1);
        queryDTO.setPageSize(5);

        CcdiProjectEmployeeCreditNegativeItemVO item = new CcdiProjectEmployeeCreditNegativeItemVO();
        item.setPersonName("李四");
        item.setPersonId("330000000000000001");
        item.setCivilCnt(1);
        item.setCivilLmt(new BigDecimal("10000.00"));

        CcdiProjectEmployeeCreditNegativePageVO pageVO = new CcdiProjectEmployeeCreditNegativePageVO();
        pageVO.setRows(List.of(item));
        pageVO.setTotal(1L);
        when(overviewService.getEmployeeCreditNegative(queryDTO)).thenReturn(pageVO);

        AjaxResult result = controller.getEmployeeCreditNegative(queryDTO);

        assertEquals(200, result.get("code"));
        assertEquals(pageVO, result.get("data"));
        verify(overviewService).getEmployeeCreditNegative(queryDTO);

        Method method = CcdiProjectOverviewController.class.getMethod(
            "getEmployeeCreditNegative",
            CcdiProjectEmployeeCreditNegativeQueryDTO.class
        );
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
        Operation operation = method.getAnnotation(Operation.class);

        assertNotNull(getMapping);
        assertEquals("/employee-credit-negative", getMapping.value()[0]);
        assertNotNull(preAuthorize);
        assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
        assertNotNull(operation);
    }

    @Test
    void shouldExposeAbnormalAccountPeopleEndpoint() throws Exception {
        CcdiProjectAbnormalAccountQueryDTO queryDTO = new CcdiProjectAbnormalAccountQueryDTO();
        queryDTO.setProjectId(40L);

        CcdiProjectAbnormalAccountPageVO pageVO = new CcdiProjectAbnormalAccountPageVO();
        when(overviewService.getAbnormalAccountPeople(queryDTO)).thenReturn(pageVO);

        AjaxResult result = controller.getAbnormalAccountPeople(queryDTO);

        assertEquals(200, result.get("code"));
        assertEquals(pageVO, result.get("data"));
        verify(overviewService).getAbnormalAccountPeople(same(queryDTO));

        Method method = CcdiProjectOverviewController.class.getMethod(
            "getAbnormalAccountPeople",
            CcdiProjectAbnormalAccountQueryDTO.class
        );
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
        Operation operation = method.getAnnotation(Operation.class);

        assertNotNull(getMapping);
        assertEquals("/abnormal-account-people", getMapping.value()[0]);
        assertNotNull(preAuthorize);
        assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
        assertNotNull(operation);
        assertEquals("查询异常账户人员信息", operation.summary());
    }

    @Test
    void shouldExposeSuspiciousTransactionsExportEndpoint() throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();
        CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
        CcdiProjectSuspiciousTransactionExcel row = new CcdiProjectSuspiciousTransactionExcel();
        row.setLeAccountName("张三");
        row.setCustomerAccountName("测试对手方");
        row.setDisplayAmount(new java.math.BigDecimal("10.00"));
        when(overviewService.exportSuspiciousTransactions(same(queryDTO))).thenReturn(List.of(row));

        controller.exportSuspiciousTransactions(response, queryDTO);

        verify(overviewService).exportSuspiciousTransactions(same(queryDTO));
        assertTrue(response.getContentType().startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
        assertTrue(response.getContentAsByteArray().length > 0);

        Method method = CcdiProjectOverviewController.class.getMethod(
            "exportSuspiciousTransactions",
            jakarta.servlet.http.HttpServletResponse.class,
            CcdiProjectSuspiciousTransactionQueryDTO.class
        );
        PostMapping postMapping = method.getAnnotation(PostMapping.class);
        Operation operation = method.getAnnotation(Operation.class);

        assertNotNull(postMapping);
        assertEquals("/suspicious-transactions/export", postMapping.value()[0]);
        assertNotNull(operation);
    }

    @Test
    void shouldExposeRiskDetailsExportEndpoint() throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();
        CcdiProjectSuspiciousTransactionQueryDTO queryDTO =
            new CcdiProjectSuspiciousTransactionQueryDTO();
        queryDTO.setProjectId(40L);
        queryDTO.setModelCode("SUSPICIOUS_GAMBLING");
        queryDTO.setSuspiciousType("MODEL_RULE");

        controller.exportRiskDetails(response, queryDTO);

        verify(overviewService).exportRiskDetails(same(response), same(queryDTO));

        Method method = CcdiProjectOverviewController.class.getMethod(
            "exportRiskDetails",
            jakarta.servlet.http.HttpServletResponse.class,
            CcdiProjectSuspiciousTransactionQueryDTO.class
        );
        PostMapping postMapping = method.getAnnotation(PostMapping.class);
        Operation operation = method.getAnnotation(Operation.class);

        assertNotNull(postMapping);
        assertEquals("/risk-details/export", postMapping.value()[0]);
        assertNotNull(operation);
    }

    @Test
    void shouldExposeRiskPeopleExportContract() throws Exception {
        MockHttpServletResponse response = new MockHttpServletResponse();
        CcdiProjectRiskPeopleOverviewExcel row = new CcdiProjectRiskPeopleOverviewExcel();
        row.setName("张三");
        row.setRiskPoint("薪酬异常");
        when(overviewService.exportRiskPeopleOverview(40L)).thenReturn(List.of(row));

        controller.exportRiskPeople(response, 40L);

        verify(overviewService).exportRiskPeopleOverview(40L);
        assertTrue(response.getContentType().startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
        assertTrue(response.getContentAsByteArray().length > 0);

        Method controllerMethod = CcdiProjectOverviewController.class.getMethod(
            "exportRiskPeople",
            jakarta.servlet.http.HttpServletResponse.class,
            Long.class
        );
        PostMapping postMapping = controllerMethod.getAnnotation(PostMapping.class);
        Operation operation = controllerMethod.getAnnotation(Operation.class);
        PreAuthorize preAuthorize = controllerMethod.getAnnotation(PreAuthorize.class);

        assertNotNull(postMapping);
        assertEquals("/risk-people/export", postMapping.value()[0]);
        assertNotNull(operation);
        assertEquals("导出风险人员总览", operation.summary());
        assertNotNull(preAuthorize);
        assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());

        Method serviceMethod = ICcdiProjectOverviewService.class.getMethod("exportRiskPeopleOverview", Long.class);
        assertEquals(List.class, serviceMethod.getReturnType());

        assertExcelColumnName("name", "姓名");
        assertExcelColumnName("idNo", "身份证号");
        assertExcelColumnName("department", "所属部门");
        assertExcelColumnName("riskCount", "疑似违规数");
        assertExcelColumnName("riskLevel", "风险等级");
        assertExcelColumnName("modelCount", "命中模型数");
        assertExcelColumnName("riskPoint", "核心异常点");
    }

    private void assertExcelColumnName(String fieldName, String expectedColumnName) throws Exception {
        Field field = CcdiProjectRiskPeopleOverviewExcel.class.getDeclaredField(fieldName);
        Excel excel = field.getAnnotation(Excel.class);
        assertNotNull(excel);
        assertEquals(expectedColumnName, excel.name());
    }
}
