package com.ruoyi.web.modules.delivery.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.delivery.constant.DeliveryConstants;
import com.ruoyi.web.modules.delivery.domain.BizShopDeliverySetting;
import com.ruoyi.web.modules.delivery.dto.DeliveryRegionDTO;
import com.ruoyi.web.modules.delivery.dto.ShopDeliverySaveDTO;
import com.ruoyi.web.modules.delivery.mapper.BizShopDeliverySettingMapper;
import com.ruoyi.web.modules.delivery.service.impl.ShopDeliveryServiceImpl;
import com.ruoyi.web.modules.delivery.vo.ShopDeliverySettingVO;
import com.ruoyi.web.modules.region.domain.BizRegion;
import com.ruoyi.web.modules.region.mapper.BizRegionMapper;

@ExtendWith(MockitoExtension.class)
class ShopDeliveryServiceImplTest
{
    private static final Long SHOP_ID = 101L;

    private static final String REGION_CODE = "540102";

    @Mock
    private BizShopDeliverySettingMapper deliverySettingMapper;

    @Mock
    private BizRegionMapper regionMapper;

    @InjectMocks
    private ShopDeliveryServiceImpl shopDeliveryService;

    @Test
    void getByShopId_nullWhenNotConfigured()
    {
        when(deliverySettingMapper.selectByShopId(SHOP_ID)).thenReturn(null);

        assertNull(shopDeliveryService.getByShopId(SHOP_ID));
    }

    @Test
    void getByShopId_returnsVo()
    {
        BizShopDeliverySetting setting = sampleSetting();
        when(deliverySettingMapper.selectByShopId(SHOP_ID)).thenReturn(setting);
        mockValidRegions();

        ShopDeliverySettingVO vo = shopDeliveryService.getByShopId(SHOP_ID);

        assertEquals("张三", vo.getShipperName());
        assertEquals(REGION_CODE, vo.getRegion().getRegionCode());
        assertEquals("拉萨市", vo.getRegion().getCityName());
        assertEquals(1, vo.getRegion().getProvinceId());
        assertEquals(2, vo.getRegion().getCityId());
        assertEquals(3, vo.getRegion().getDistrictId());
    }

    @Test
    void save_insertWhenFirstTime()
    {
        mockValidRegions();
        when(deliverySettingMapper.selectByShopId(SHOP_ID)).thenReturn(null);
        when(deliverySettingMapper.insert(any())).thenReturn(1);

        shopDeliveryService.save(SHOP_ID, validDto(), "seller01");

        verify(deliverySettingMapper).insert(org.mockito.ArgumentMatchers.argThat(row ->
                REGION_CODE.equals(row.getRegionCode())
                        && "西藏自治区/拉萨市/城关区".equals(row.getRegionName())
                        && "拉萨市".equals(row.getCityName())));
        verify(deliverySettingMapper, never()).updateByShopId(any());
    }

    @Test
    void save_updateWhenExists()
    {
        mockValidRegions();
        when(deliverySettingMapper.selectByShopId(SHOP_ID)).thenReturn(sampleSetting());
        when(deliverySettingMapper.updateByShopId(any())).thenReturn(1);

        shopDeliveryService.save(SHOP_ID, validDto(), "seller01");

        verify(deliverySettingMapper).updateByShopId(any(BizShopDeliverySetting.class));
        verify(deliverySettingMapper, never()).insert(any());
    }

    @Test
    void save_invalidMobile_throws()
    {
        ShopDeliverySaveDTO dto = validDto();
        dto.setShipperMobile("123");

        ServiceException ex = assertThrows(ServiceException.class,
                () -> shopDeliveryService.save(SHOP_ID, dto, "seller01"));
        assertEquals(DeliveryConstants.MSG_SHIPPER_MOBILE_INVALID, ex.getMessage());
    }

    @Test
    void save_invalidRegionCode_throws()
    {
        when(regionMapper.selectByCode(540102)).thenReturn(
                region(3, 2, DeliveryConstants.REGION_TYPE_DISTRICT, "城关区", 540102));
        when(regionMapper.selectById(2)).thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> shopDeliveryService.save(SHOP_ID, validDto(), "seller01"));
        assertEquals(DeliveryConstants.MSG_REGION_INVALID, ex.getMessage());
    }

    @Test
    void save_nullRegion_throws()
    {
        ShopDeliverySaveDTO dto = validDto();
        dto.setRegion(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> shopDeliveryService.save(SHOP_ID, dto, "seller01"));
        assertEquals(DeliveryConstants.MSG_REGION_INVALID, ex.getMessage());
    }

    private void mockValidRegions()
    {
        when(regionMapper.selectByCode(540102)).thenReturn(
                region(3, 2, DeliveryConstants.REGION_TYPE_DISTRICT, "城关区", 540102));
        when(regionMapper.selectById(2)).thenReturn(
                region(2, 1, DeliveryConstants.REGION_TYPE_CITY, "拉萨市", 540100));
        when(regionMapper.selectById(1)).thenReturn(
                region(1, 0, DeliveryConstants.REGION_TYPE_PROVINCE, "西藏自治区", 540000));
    }

    private static ShopDeliverySaveDTO validDto()
    {
        DeliveryRegionDTO region = new DeliveryRegionDTO();
        region.setRegionCode(REGION_CODE);
        ShopDeliverySaveDTO dto = new ShopDeliverySaveDTO();
        dto.setShipperName("张三");
        dto.setShipperMobile("13800138000");
        dto.setRegion(region);
        dto.setDetailAddress("某某路 1 号");
        return dto;
    }

    private static BizShopDeliverySetting sampleSetting()
    {
        BizShopDeliverySetting setting = new BizShopDeliverySetting();
        setting.setSettingId(10L);
        setting.setShopId(SHOP_ID);
        setting.setShipperName("张三");
        setting.setShipperMobile("13800138000");
        setting.setRegionCode(REGION_CODE);
        setting.setRegionName("西藏自治区/拉萨市/城关区");
        setting.setCityName("拉萨市");
        setting.setDetailAddress("某某路 1 号");
        return setting;
    }

    private static BizRegion region(int id, int pid, int type, String name, int code)
    {
        BizRegion region = new BizRegion();
        region.setId(id);
        region.setPid(pid);
        region.setType(type);
        region.setName(name);
        region.setCode(code);
        return region;
    }
}
