package com.ruoyi.ccdi.project.controller;

import io.swagger.v3.oas.annotations.Operation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

class CcdiProjectSpecialCheckExtendedQueryContractTest {

    @Test
    void shouldExposeExtendedPurchaseEndpointsAndContracts() throws Exception {
        assertEndpoint(
            "getExtendedPurchaseList",
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedPurchaseQueryDTO",
            "/extended-query/purchase/list"
        );
        assertEndpoint(
            "getExtendedPurchaseDetail",
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedPurchaseDetailQueryDTO",
            "/extended-query/purchase/detail"
        );

        assertFields(
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedPurchaseQueryDTO",
            "projectId",
            "applicantName",
            "applyDateStart",
            "applyDateEnd",
            "pageNum",
            "pageSize"
        );
        assertFields(
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedPurchaseDetailQueryDTO",
            "projectId",
            "purchaseId"
        );
        assertFields(
            "com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedPurchaseListVO",
            "rows",
            "total"
        );
        assertExactFields(
            "com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedPurchaseListItemVO",
            "purchaseId",
            "projectName",
            "subjectName",
            "applicantName",
            "applyDate"
        );
        assertDetailVoOwnedByProject("com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedPurchaseDetailVO");
    }

    @Test
    void shouldExposeExtendedRecruitmentEndpointsAndContracts() throws Exception {
        assertEndpoint(
            "getExtendedRecruitmentList",
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedRecruitmentQueryDTO",
            "/extended-query/recruitment/list"
        );
        assertEndpoint(
            "getExtendedRecruitmentDetail",
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedRecruitmentDetailQueryDTO",
            "/extended-query/recruitment/detail"
        );

        assertFields(
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedRecruitmentQueryDTO",
            "projectId",
            "interviewerName",
            "pageNum",
            "pageSize"
        );
        assertFields(
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedRecruitmentDetailQueryDTO",
            "projectId",
            "recruitId"
        );
        assertFields(
            "com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedRecruitmentListVO",
            "rows",
            "total"
        );
        assertExactFields(
            "com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedRecruitmentListItemVO",
            "recruitId",
            "recruitName",
            "posName",
            "interviewerNameSummary",
            "admitStatus"
        );
        assertDetailVoOwnedByProject("com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedRecruitmentDetailVO");
    }

    @Test
    void shouldExposeExtendedTransferEndpointsAndContracts() throws Exception {
        assertEndpoint(
            "getExtendedTransferList",
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedTransferQueryDTO",
            "/extended-query/transfer/list"
        );
        assertEndpoint(
            "getExtendedTransferDetail",
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedTransferDetailQueryDTO",
            "/extended-query/transfer/detail"
        );

        assertFields(
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedTransferQueryDTO",
            "projectId",
            "staffName",
            "transferDateStart",
            "transferDateEnd",
            "pageNum",
            "pageSize"
        );
        assertFields(
            "com.ruoyi.ccdi.project.domain.dto.CcdiProjectExtendedTransferDetailQueryDTO",
            "projectId",
            "id"
        );
        assertFields(
            "com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedTransferListVO",
            "rows",
            "total"
        );
        assertExactFields(
            "com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedTransferListItemVO",
            "id",
            "staffName",
            "transferType",
            "deptNameBefore",
            "deptNameAfter",
            "transferDate"
        );
        assertDetailVoOwnedByProject("com.ruoyi.ccdi.project.domain.vo.CcdiProjectExtendedTransferDetailVO");
    }

    private void assertEndpoint(String methodName, String dtoClassName, String expectedPath) throws Exception {
        Class<?> controllerClass = Class.forName("com.ruoyi.ccdi.project.controller.CcdiProjectSpecialCheckController");
        Class<?> dtoClass = Class.forName(dtoClassName);
        Method method = controllerClass.getMethod(methodName, dtoClass);

        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
        Operation operation = method.getAnnotation(Operation.class);

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

    private void assertFields(String className, String... expectedFields) throws Exception {
        Class<?> type = Class.forName(className);
        List<String> fieldNames = Arrays.stream(type.getDeclaredFields()).map(Field::getName).collect(Collectors.toList());
        for (String expectedField : expectedFields) {
            assertTrue(fieldNames.contains(expectedField), className + " 缺少字段 " + expectedField);
        }
    }

    private void assertExactFields(String className, String... expectedFields) throws Exception {
        Class<?> type = Class.forName(className);
        List<String> fieldNames = Arrays.stream(type.getDeclaredFields()).map(Field::getName).collect(Collectors.toList());

        assertEquals(expectedFields.length, fieldNames.size(), className + " 字段数量不符合预期");
        for (String expectedField : expectedFields) {
            assertTrue(fieldNames.contains(expectedField), className + " 缺少字段 " + expectedField);
        }
    }

    private void assertDetailVoOwnedByProject(String className) throws Exception {
        Class<?> type = Class.forName(className);
        assertTrue(type.getName().startsWith("com.ruoyi.ccdi.project.domain.vo."));
        assertTrue(!type.getName().contains("info.collection"));
    }
}
