package com.ruoyi.web.modules.industryservice.support;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
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 org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.system.service.ISysDeptService;

@ExtendWith(MockitoExtension.class)
@DisplayName("TownDeptSupport 乡镇/村范围")
class TownDeptSupportScopeTest
{
    @Mock
    private ISysDeptService deptService;

    @InjectMocks
    private TownDeptSupport townDeptSupport;

    @AfterEach
    void clearSecurity()
    {
        SecurityContextHolder.clearContext();
    }

    @Test
    @DisplayName("村级人员：乡镇下拉仅上级镇，村树仅本村")
    void villageUserSeesParentTownAndOwnVillage()
    {
        SysDept town = town(200L, "雅安镇", 20);
        SysDept village = village(300L, "雅安镇荣嘎村", 200L);
        loginAs(300L, 9L);

        when(deptService.selectDeptById(300L)).thenReturn(village);
        when(deptService.selectDeptById(200L)).thenReturn(town);
        when(deptService.selectDeptById(YakHerdInventoryRules.TOWN_PARENT_ID)).thenReturn(county());

        List<SysDept> towns = townDeptSupport.listReportTowns();
        assertEquals(1, towns.size());
        assertEquals(200L, towns.get(0).getDeptId());

        List<SysDept> villages = townDeptSupport.listVillagesUnderTown(200L);
        assertEquals(1, villages.size());
        assertEquals(300L, villages.get(0).getDeptId());

        assertTrue(townDeptSupport.listVillagesUnderTown(999L).isEmpty());

        List<TreeSelect> tree = townDeptSupport.buildVillageTree();
        assertEquals(1, tree.size());
        assertEquals(1, tree.get(0).getChildren().size());
        assertEquals(200L, tree.get(0).getChildren().get(0).getId());
        assertEquals(1, tree.get(0).getChildren().get(0).getChildren().size());
        assertEquals(300L, tree.get(0).getChildren().get(0).getChildren().get(0).getId());
    }

    @Test
    @DisplayName("乡镇人员：仅本镇")
    void townUserSeesOwnTown()
    {
        SysDept town = town(200L, "雅安镇", 20);
        loginAs(200L, 8L);
        when(deptService.selectDeptById(200L)).thenReturn(town);

        List<SysDept> towns = townDeptSupport.listReportTowns();
        assertEquals(1, towns.size());
        assertEquals(200L, towns.get(0).getDeptId());
    }

    @Test
    @DisplayName("无登录上下文时仍走部门列表查询")
    void noLoginFallsBackToDeptList()
    {
        SysDept town = town(200L, "雅安镇", 20);
        when(deptService.selectDeptList(any())).thenReturn(Collections.singletonList(town));
        assertEquals(1, townDeptSupport.listReportTowns().size());
    }

    @Test
    @DisplayName("全县口径：listAllReportTowns 不依赖登录人收窄")
    void allReportTownsIgnoresLoginScope()
    {
        SysDept townA = town(200L, "雅安镇", 20);
        SysDept townB = town(201L, "巴青镇", 21);
        SysDept village = village(300L, "雅安镇荣嘎村", 200L);
        loginAs(300L, 9L);
        when(deptService.selectDeptById(any())).thenAnswer(invocation -> {
            Long id = invocation.getArgument(0);
            if (Long.valueOf(300L).equals(id))
            {
                return village;
            }
            if (Long.valueOf(200L).equals(id))
            {
                return townA;
            }
            if (Long.valueOf(201L).equals(id))
            {
                return townB;
            }
            if (Long.valueOf(YakHerdInventoryRules.TOWN_PARENT_ID).equals(id))
            {
                return county();
            }
            return null;
        });
        when(deptService.selectDeptListWithoutDataScope(any())).thenAnswer(invocation -> {
            SysDept q = invocation.getArgument(0);
            if (Long.valueOf(YakHerdInventoryRules.TOWN_PARENT_ID).equals(q.getParentId()))
            {
                return java.util.Arrays.asList(townA, townB);
            }
            if (Long.valueOf(200L).equals(q.getParentId()))
            {
                return Collections.singletonList(village);
            }
            return Collections.emptyList();
        });

        assertEquals(2, townDeptSupport.listAllReportTowns().size());
        assertEquals(1, townDeptSupport.listAllVillagesUnderTown(200L).size());
        assertEquals(2, townDeptSupport.toAllOptionList().size());

        List<TreeSelect> tree = townDeptSupport.buildAllVillageTree();
        assertEquals(1, tree.size());
        assertEquals(2, tree.get(0).getChildren().size());
    }

    private static void loginAs(Long deptId, Long userId)
    {
        SysUser user = new SysUser();
        user.setUserId(userId);
        user.setDeptId(deptId);
        LoginUser loginUser = new LoginUser(userId, deptId, user, Collections.emptySet());
        SecurityContextHolder.getContext().setAuthentication(
                new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList()));
    }

    private static SysDept county()
    {
        SysDept d = new SysDept();
        d.setDeptId(YakHerdInventoryRules.TOWN_PARENT_ID);
        d.setDeptName("巴青县");
        d.setStatus("0");
        d.setDelFlag("0");
        return d;
    }

    private static SysDept town(Long id, String name, int orderNum)
    {
        SysDept d = new SysDept();
        d.setDeptId(id);
        d.setDeptName(name);
        d.setParentId(YakHerdInventoryRules.TOWN_PARENT_ID);
        d.setOrderNum(orderNum);
        d.setStatus("0");
        d.setDelFlag("0");
        return d;
    }

    private static SysDept village(Long id, String name, Long townId)
    {
        SysDept d = new SysDept();
        d.setDeptId(id);
        d.setDeptName(name);
        d.setParentId(townId);
        d.setOrderNum(1);
        d.setStatus("0");
        d.setDelFlag("0");
        return d;
    }
}
