package com.ruoyi.app.pay;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.domain.InfoUser;
import com.ruoyi.system.domain.PosOrder;
import com.ruoyi.system.domain.PosOrderLinePayment;
import com.ruoyi.system.service.IPosOrderLinePaymentService;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class LinePayOrderGuardTest {

    @Test
    void merchantOwnershipUsesShIdForRegularAndNightMarketUsers() {
        LinePayOrderGuard guard = new LinePayOrderGuard(mock(IPosOrderLinePaymentService.class));
        PosOrder order = new PosOrder();
        order.setShId(10L);
        InfoUser merchant = user(10L, "1", null);

        assertDoesNotThrow(() -> guard.requireMerchantOwnership(order, merchant));
        merchant.setUserId(11L);
        assertThrows(ServiceException.class, () -> guard.requireMerchantOwnership(order, merchant));
    }

    @Test
    void stallMerchantOwnershipUsesStoreIdAndLineOrderMustBePaidBeforeAccept() {
        IPosOrderLinePaymentService payments = mock(IPosOrderLinePaymentService.class);
        LinePayOrderGuard guard = new LinePayOrderGuard(payments);
        PosOrder order = new PosOrder();
        order.setDdId("DD-1");
        order.setMdId(20L);
        order.setPayType("3");
        order.setPayStatus(0L);
        InfoUser stall = user(30L, "9", 20L);
        PosOrderLinePayment line = new PosOrderLinePayment();
        line.setStatus("WAITING_AUTH");
        when(payments.getByDdId("DD-1")).thenReturn(List.of(line));

        assertDoesNotThrow(() -> guard.requireMerchantOwnership(order, stall));
        assertThrows(ServiceException.class, () -> guard.requirePaidBeforeAccept(order));

        order.setPayStatus(1L);
        assertDoesNotThrow(() -> guard.requirePaidBeforeAccept(order));
    }

    @Test
    void historicalPayTypeThreeWithoutLineAttemptIsNotTreatedAsLinePay() {
        IPosOrderLinePaymentService payments = mock(IPosOrderLinePaymentService.class);
        LinePayOrderGuard guard = new LinePayOrderGuard(payments);
        PosOrder historical = new PosOrder();
        historical.setDdId("OLD-ZALO");
        historical.setPayType("3");
        historical.setPayStatus(0L);
        when(payments.getByDdId("OLD-ZALO")).thenReturn(List.of());

        assertDoesNotThrow(() -> guard.requirePaidBeforeAccept(historical));
    }

    @Test
    void ordinaryUserAndRiderCannotUseMerchantCancellationOwnership() {
        LinePayOrderGuard guard = new LinePayOrderGuard(mock(IPosOrderLinePaymentService.class));
        PosOrder order = new PosOrder();
        order.setMdId(20L);

        assertThrows(ServiceException.class,
                () -> guard.requireMerchantOwnership(order, user(1L, "0", 20L)));
        assertThrows(ServiceException.class,
                () -> guard.requireMerchantOwnership(order, user(2L, "2", 20L)));
    }

    @Test
    void legacyLineOrderAllowsOnlyOwnerMerchantOrAssignedRiderTransition() {
        LinePayOrderGuard guard = new LinePayOrderGuard(mock(IPosOrderLinePaymentService.class));
        PosOrder order = new PosOrder();
        order.setShId(10L);
        order.setQsId(30L);

        assertDoesNotThrow(() -> guard.requireLegacyOrderActor(order, user(10L, "1", null), 2L));
        assertDoesNotThrow(() -> guard.requireLegacyOrderActor(order, user(30L, "2", null), 4L));
        assertThrows(ServiceException.class,
                () -> guard.requireLegacyOrderActor(order, user(31L, "2", null), 4L));
        assertThrows(ServiceException.class,
                () -> guard.requireLegacyOrderActor(order, user(30L, "2", null), 2L));
        order.setQsId(null);
        assertThrows(ServiceException.class,
                () -> guard.requireLegacyOrderActor(order, user(30L, "2", null), 4L));
        assertThrows(ServiceException.class,
                () -> guard.requireLegacyOrderActor(order, user(99L, "0", null), 0L));
    }

    private static InfoUser user(Long id, String type, Long storeId) {
        InfoUser user = new InfoUser();
        user.setUserId(id);
        user.setUserType(type);
        user.setStoreId(storeId);
        return user;
    }
}
