package com.ruoyi.system.service;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.system.domain.InfoUser;
import com.ruoyi.system.domain.PosFood;
import com.ruoyi.system.domain.PosOrder;
import com.ruoyi.system.domain.PosStore;
import com.ruoyi.system.mapper.InfoUserMapper;
import com.ruoyi.system.mapper.MerchantSubaccountStoreMapper;
import com.ruoyi.system.mapper.PosFoodMapper;
import com.ruoyi.system.mapper.PosOrderMapper;
import com.ruoyi.system.mapper.PosStoreMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class MerchantStoreAccessServiceTest {

    @Mock private InfoUserMapper infoUserMapper;
    @Mock private MerchantSubaccountStoreMapper relationMapper;
    @Mock private PosStoreMapper posStoreMapper;
    @Mock private PosOrderMapper posOrderMapper;
    @Mock private PosFoodMapper posFoodMapper;

    private MockedStatic<MessageUtils> messages;
    private MerchantStoreAccessService service;

    @BeforeEach
    void setUp() {
        messages = mockStatic(MessageUtils.class);
        messages.when(() -> MessageUtils.message(org.mockito.ArgumentMatchers.anyString()))
                .thenAnswer(invocation -> invocation.getArgument(0));
        service = new MerchantStoreAccessService(infoUserMapper, relationMapper,
                posStoreMapper, posOrderMapper, posFoodMapper);
    }

    @AfterEach
    void tearDown() {
        messages.close();
    }

    @Test
    void ownerReceivesAllCurrentlyOwnedStores() {
        when(infoUserMapper.selectInfoUserByUserId(1L)).thenReturn(user(1L, "1", "0", null, null));
        when(posStoreMapper.selectOwnedStoreIdsByUserId(1L)).thenReturn(List.of(10L, 11L));

        MerchantAccessContext context = service.resolve(1L);

        assertEquals(1L, context.ownerUserId());
        assertEquals(Set.of(10L, 11L), context.storeIds());
        assertEquals(true, context.owner());
    }

    @Test
    void subaccountOnlyReceivesAuthorizedStoresStillOwnedByItsOwner() {
        when(infoUserMapper.selectInfoUserByUserId(5L)).thenReturn(user(5L, "5", "0", "0", 1L));
        when(infoUserMapper.selectInfoUserByUserId(1L)).thenReturn(user(1L, "1", "0", null, null));
        when(relationMapper.selectStoreIdsBySubaccountUserId(5L)).thenReturn(List.of(10L, 11L));
        when(posStoreMapper.selectPosStoreById(10L)).thenReturn(store(10, 1L, "0"));
        when(posStoreMapper.selectPosStoreById(11L)).thenReturn(store(11, 2L, "0"));

        assertEquals(Set.of(10L), service.getAccessibleStoreIds(5L));
    }

    @Test
    void disabledSubaccountOrOwnerIsRejected() {
        when(infoUserMapper.selectInfoUserByUserId(5L)).thenReturn(user(5L, "5", "0", "1", 1L));
        assertThrows(ServiceException.class, () -> service.resolve(5L));

        when(infoUserMapper.selectInfoUserByUserId(6L)).thenReturn(user(6L, "5", "0", "0", 2L));
        when(infoUserMapper.selectInfoUserByUserId(2L)).thenReturn(user(2L, "1", "1", null, null));
        assertThrows(ServiceException.class, () -> service.resolve(6L));
    }

    @Test
    void requireOwnerRejectsSubaccount() {
        when(infoUserMapper.selectInfoUserByUserId(5L)).thenReturn(user(5L, "5", "0", "0", 1L));
        when(infoUserMapper.selectInfoUserByUserId(1L)).thenReturn(user(1L, "1", "0", null, null));
        when(relationMapper.selectStoreIdsBySubaccountUserId(5L)).thenReturn(List.of());

        assertThrows(ServiceException.class, () -> service.requireOwner(5L));
    }

    @Test
    void orderAccessUsesPersistedOrderStoreInsteadOfRequestStore() {
        when(infoUserMapper.selectInfoUserByUserId(5L)).thenReturn(user(5L, "5", "0", "0", 1L));
        when(infoUserMapper.selectInfoUserByUserId(1L)).thenReturn(user(1L, "1", "0", null, null));
        when(relationMapper.selectStoreIdsBySubaccountUserId(5L)).thenReturn(List.of(10L));
        when(posStoreMapper.selectPosStoreById(10L)).thenReturn(store(10, 1L, "0"));
        PosOrder order = new PosOrder();
        order.setId(100L);
        order.setMdId(11L);
        when(posOrderMapper.selectPosOrderById(100L)).thenReturn(order);

        assertThrows(ServiceException.class, () -> service.requireOrderAccess(5L, 100L));
    }

    @Test
    void foodAccessUsesPersistedFoodStore() {
        when(infoUserMapper.selectInfoUserByUserId(5L)).thenReturn(user(5L, "5", "0", "0", 1L));
        when(infoUserMapper.selectInfoUserByUserId(1L)).thenReturn(user(1L, "1", "0", null, null));
        when(relationMapper.selectStoreIdsBySubaccountUserId(5L)).thenReturn(List.of(10L));
        when(posStoreMapper.selectPosStoreById(10L)).thenReturn(store(10, 1L, "0"));
        PosFood food = new PosFood();
        food.setId(200L);
        food.setMdid(10L);
        when(posFoodMapper.selectPosFoodById(200L)).thenReturn(food);

        assertEquals(food, service.requireFoodAccess(5L, 200L));
    }

    private InfoUser user(Long id, String type, String status, String subaccountStatus, Long ownerId) {
        InfoUser user = new InfoUser();
        user.setUserId(id);
        user.setUserType(type);
        user.setStatus(status);
        user.setSubaccountStatus(subaccountStatus);
        user.setMerchantOwnerId(ownerId);
        user.setDelFlag("0");
        return user;
    }

    private PosStore store(Integer id, Long userId, String delFlag) {
        PosStore store = new PosStore();
        store.setId(id);
        store.setUserId(userId);
        store.setDelFlag(delFlag);
        return store;
    }
}
