package com.ruoyi.app.order;

import com.ruoyi.app.order.dto.AdminOrderActionRequest;
import com.ruoyi.app.order.dto.AdminOrderStatusUpdateRequest;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.domain.PosOrder;
import com.ruoyi.system.domain.PosOrderLinePayment;
import com.ruoyi.system.domain.InfoUser;
import com.ruoyi.system.service.IInfoUserService;
import com.ruoyi.system.service.IPosOrderLinePaymentService;
import com.ruoyi.system.service.IPosOrderService;
import com.ruoyi.system.service.MerchantStoreAccessService;
import com.ruoyi.system.utils.AuthContext;
import com.ruoyi.system.utils.JwtUtil;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.test.util.ReflectionTestUtils;

import java.lang.reflect.Method;
import java.util.Arrays;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

class PosOrderAdminStatusControllerTest {

    @Test
    void statusEndpointsRequireOrderEditPermission() {
        for (Method method : PosOrderController.class.getDeclaredMethods()) {
            if (method.getName().startsWith("admin") || method.getName().equals("getStatusContext")) {
                PreAuthorize annotation = method.getAnnotation(PreAuthorize.class);
                assertNotNull(annotation, method.getName() + " must require permission");
                if (method.getName().equals("adminReconcileLinePayment")) {
                    assertTrue(annotation.value().contains("system:order:linePaymentReconcile"));
                } else if (method.getName().equals("adminRefundLinePayment")) {
                    assertTrue(annotation.value().contains("system:order:lineRefund"));
                } else {
                    assertTrue(annotation.value().contains("system:order:edit"));
                }
            }
        }
    }

    @Test
    void dedicatedRequestsCannotCarryBusinessFields() {
        assertFalse(hasField(AdminOrderActionRequest.class, "amount"));
        assertFalse(hasField(AdminOrderActionRequest.class, "payType"));
        assertFalse(hasField(AdminOrderStatusUpdateRequest.class, "payStatus"));
        assertFalse(hasField(AdminOrderStatusUpdateRequest.class, "sdTime"));
    }

    @Test
    void legacyEditRejectsProtectedStatusFields() {
        PosOrder order = new PosOrder();
        order.setId(1L);
        order.setState(2L);

        AjaxResult result = new PosOrderController().edit(order);

        assertEquals(500, result.get(AjaxResult.CODE_TAG));
        assertEquals("订单状态请使用状态专用操作", result.get(AjaxResult.MSG_TAG));
    }

    @Test
    void legacyStateEndpointRejectsRealLineOrderBeforeApplyingClientFields() {
        PosOrderController controller = new PosOrderController();
        IPosOrderService orderService = mock(IPosOrderService.class);
        IPosOrderLinePaymentService paymentService = mock(IPosOrderLinePaymentService.class);
        ReflectionTestUtils.setField(controller, "posOrderService", orderService);
        ReflectionTestUtils.setField(controller, "linePaymentService", paymentService);
        IInfoUserService users = mock(IInfoUserService.class);
        ReflectionTestUtils.setField(controller, "merchantStoreAccessService", mock(MerchantStoreAccessService.class));
        ReflectionTestUtils.setField(controller, "infoUserService", users);
        PosOrder stored = new PosOrder();
        stored.setId(1L);
        stored.setDdId("D-1");
        stored.setPayType("3");
        PosOrderLinePayment payment = new PosOrderLinePayment();
        payment.setDdId("D-1");
        when(orderService.getById(1L)).thenReturn(stored);
        InfoUser merchant = new InfoUser();
        merchant.setUserId(9L);
        merchant.setUserType("1");
        when(users.getById(9L)).thenReturn(merchant);
        when(paymentService.getByDdId("D-1")).thenReturn(java.util.List.of(payment));
        PosOrder malicious = new PosOrder();
        malicious.setId(1L);
        malicious.setState(7L);
        malicious.setPoints(999);

        try (MockedStatic<AuthContext> auth = mockStatic(AuthContext.class)) {
            auth.when(AuthContext::requireUserId).thenReturn(9L);
            assertThrows(ServiceException.class,
                    () -> controller.setorderuzt(JwtUtil.token("9", "tester"), malicious));
        }
    }

    private boolean hasField(Class<?> type, String name) {
        return Arrays.stream(type.getDeclaredFields()).anyMatch(field -> field.getName().equals(name))
                || (type.getSuperclass() != null && type.getSuperclass() != Object.class
                && hasField(type.getSuperclass(), name));
    }
}
