package com.ruoyi.app.order;

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.system.domain.InfoUser;
import com.ruoyi.system.domain.PosOrder;
import com.ruoyi.system.service.IInfoUserService;
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 com.ruoyi.common.utils.MessageUtils;
import org.junit.jupiter.api.AfterEach;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.ArgumentCaptor;
import org.springframework.test.util.ReflectionTestUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class OrderListPaymentFilterTest {
    private MockedStatic<MessageUtils> messages;
    private MockedStatic<AuthContext> auth;

    @BeforeAll
    static void initializeTableMetadata() {
        TableInfoHelper.initTableInfo(
                new MapperBuilderAssistant(new MybatisConfiguration(), ""), PosOrder.class);
    }

    @Test
    void userUnpaidTabIncludesOnlinePickupAndDineInOrders() {
        IPosOrderService orders = mock(IPosOrderService.class);
        UserOrderController controller = new UserOrderController();
        ReflectionTestUtils.setField(controller, "posOrderService", orders);
        when(orders.page(any(Page.class), any(Wrapper.class))).thenReturn(new Page<>());

        controller.orderList(tokenFor(909L), 1, 10, "unpaid", null);

        LambdaQueryWrapper<PosOrder> query = capturedQuery(orders);
        String sql = query.getSqlSegment();
        Collection<Object> parameters = new ArrayList<>(query.getParamNameValuePairs().values());
        assertTrue(sql.contains("pay_type IN"));
        assertFalse(sql.contains("AND type ="));
        assertEquals(1, Collections.frequency(parameters, "2"));
        assertEquals(1, Collections.frequency(parameters, "3"));
    }

    @Test
    void userActiveTabAllowsUnpaidPickupAndDineInOnlyForCashPayment() {
        IPosOrderService orders = mock(IPosOrderService.class);
        UserOrderController controller = new UserOrderController();
        ReflectionTestUtils.setField(controller, "posOrderService", orders);
        when(orders.page(any(Page.class), any(Wrapper.class))).thenReturn(new Page<>());

        controller.orderList(tokenFor(909L), 1, 10, "active", null);

        LambdaQueryWrapper<PosOrder> query = capturedQuery(orders);
        String sql = query.getSqlSegment();
        Collection<Object> parameters = new ArrayList<>(query.getParamNameValuePairs().values());
        assertTrue(sql.contains("(pay_status ="));
        assertTrue(sql.contains("OR (pay_type ="));
        assertTrue(sql.contains("AND type IN"));
        assertEquals(1, Collections.frequency(parameters, "1"));
    }

    @Test
    void merchantPendingTabDoesNotExposeUnpaidOnlinePickupOrDineInOrders() {
        IPosOrderService orders = mock(IPosOrderService.class);
        IInfoUserService users = mock(IInfoUserService.class);
        PosOrderShOprateController controller = new PosOrderShOprateController();
        ReflectionTestUtils.setField(controller, "posOrderService", orders);
        ReflectionTestUtils.setField(controller, "infoUserService", users);
        MerchantStoreAccessService access = mock(MerchantStoreAccessService.class);
        auth.when(AuthContext::requireUserId).thenReturn(801L);
        when(access.getAccessibleStoreIds(801L)).thenReturn(Set.of(11L, 12L));
        ReflectionTestUtils.setField(controller, "merchantStoreAccessService", access);
        InfoUser merchant = new InfoUser();
        merchant.setUserId(801L);
        merchant.setUserType("1");
        when(users.getOne(any())).thenReturn(merchant);
        when(orders.page(any(Page.class), any(Wrapper.class))).thenReturn(new Page<>());

        controller.orderList(tokenFor(801L), 1, 10, "pending", null, null);

        LambdaQueryWrapper<PosOrder> query = capturedQuery(orders);
        String sql = query.getSqlSegment();
        Collection<Object> parameters = new ArrayList<>(query.getParamNameValuePairs().values());
        assertTrue(sql.contains("(pay_status ="));
        assertTrue(sql.contains("OR (pay_type ="));
        assertTrue(sql.contains("AND type IN"));
        assertEquals(1, Collections.frequency(parameters, "1"));
    }

    @BeforeEach
    void setUpMessages() {
        messages = mockStatic(MessageUtils.class);
        messages.when(() -> MessageUtils.message(anyString()))
                .thenAnswer(invocation -> invocation.getArgument(0));
        auth = mockStatic(AuthContext.class);
    }

    @AfterEach
    void closeMessages() {
        auth.close();
        messages.close();
    }

    @Test
    void subaccountOrderListFiltersByAllAuthorizedStores() {
        IPosOrderService orders = mock(IPosOrderService.class);
        IInfoUserService users = mock(IInfoUserService.class);
        MerchantStoreAccessService access = mock(MerchantStoreAccessService.class);
        PosOrderShOprateController controller = new PosOrderShOprateController();
        ReflectionTestUtils.setField(controller, "posOrderService", orders);
        ReflectionTestUtils.setField(controller, "infoUserService", users);
        ReflectionTestUtils.setField(controller, "merchantStoreAccessService", access);
        InfoUser subaccount = new InfoUser();
        subaccount.setUserId(501L);
        subaccount.setUserType("5");
        auth.when(AuthContext::requireUserId).thenReturn(501L);
        when(users.getOne(any())).thenReturn(subaccount);
        when(access.getAccessibleStoreIds(501L)).thenReturn(Set.of(11L, 12L));
        when(orders.page(any(Page.class), any(Wrapper.class))).thenReturn(new Page<>());

        controller.orderList(tokenFor(501L), 1, 10, "", null, null);

        LambdaQueryWrapper<PosOrder> query = capturedQuery(orders);
        assertTrue(query.getSqlSegment().contains("md_id IN"));
        assertTrue(query.getParamNameValuePairs().containsValue(11L));
        assertTrue(query.getParamNameValuePairs().containsValue(12L));
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private static LambdaQueryWrapper<PosOrder> capturedQuery(IPosOrderService orders) {
        ArgumentCaptor<Wrapper> captor = ArgumentCaptor.forClass(Wrapper.class);
        verify(orders).page(any(Page.class), captor.capture());
        return (LambdaQueryWrapper<PosOrder>) captor.getValue();
    }

    private static String tokenFor(Long userId) {
        return JwtUtil.setToken(String.valueOf(userId), "test-user");
    }
}
