package tech.easyflow.admin.service.system;

import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.stereotype.Service;
import tech.easyflow.admin.model.SystemFormOptionsView;
import tech.easyflow.approval.entity.vo.ApprovalFlowScopeVo;
import tech.easyflow.approval.enums.ApprovalScopeType;
import tech.easyflow.agent.entity.AgentCategory;
import tech.easyflow.agent.service.AgentCategoryService;
import tech.easyflow.ai.entity.DocumentCollectionCategory;
import tech.easyflow.ai.entity.PluginCategory;
import tech.easyflow.ai.entity.ResourceCategory;
import tech.easyflow.ai.entity.WorkflowCategory;
import tech.easyflow.ai.service.DocumentCollectionCategoryService;
import tech.easyflow.ai.service.PluginCategoryService;
import tech.easyflow.ai.service.ResourceCategoryService;
import tech.easyflow.ai.service.WorkflowCategoryService;
import tech.easyflow.common.constant.enums.EnumDataStatus;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.entity.SysAccount;
import tech.easyflow.system.entity.SysDept;
import tech.easyflow.system.entity.SysMenu;
import tech.easyflow.system.entity.SysPosition;
import tech.easyflow.system.entity.SysRole;
import tech.easyflow.system.entity.vo.SysRoleCategoryScopeItemVo;
import tech.easyflow.system.service.SysDeptService;
import tech.easyflow.system.service.SysMenuService;
import tech.easyflow.system.service.SysPositionService;
import tech.easyflow.system.service.SysRoleService;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

/**
 * 查询并校验系统管理表单所需的安全选项。
 */
@Service
public class SystemFormOptionService {

    private final AgentCategoryService agentCategoryService;
    private final WorkflowCategoryService workflowCategoryService;
    private final DocumentCollectionCategoryService documentCollectionCategoryService;
    private final PluginCategoryService pluginCategoryService;
    private final ResourceCategoryService resourceCategoryService;
    private final SysDeptService sysDeptService;
    private final SysMenuService sysMenuService;
    private final SysRoleService sysRoleService;
    private final SysPositionService sysPositionService;

    /**
     * 创建系统表单选项服务。
     *
     * @param agentCategoryService Agent 分类服务
     * @param workflowCategoryService 工作流分类服务
     * @param documentCollectionCategoryService 知识库分类服务
     * @param pluginCategoryService 插件分类服务
     * @param resourceCategoryService 素材分类服务
     * @param sysDeptService 部门服务
     * @param sysMenuService 菜单服务
     * @param sysRoleService 角色服务
     * @param sysPositionService 岗位服务
     */
    public SystemFormOptionService(
            AgentCategoryService agentCategoryService,
            WorkflowCategoryService workflowCategoryService,
            DocumentCollectionCategoryService documentCollectionCategoryService,
            PluginCategoryService pluginCategoryService,
            ResourceCategoryService resourceCategoryService,
            SysDeptService sysDeptService,
            SysMenuService sysMenuService,
            SysRoleService sysRoleService,
            SysPositionService sysPositionService) {
        this.agentCategoryService = agentCategoryService;
        this.workflowCategoryService = workflowCategoryService;
        this.documentCollectionCategoryService = documentCollectionCategoryService;
        this.pluginCategoryService = pluginCategoryService;
        this.resourceCategoryService = resourceCategoryService;
        this.sysDeptService = sysDeptService;
        this.sysMenuService = sysMenuService;
        this.sysRoleService = sysRoleService;
        this.sysPositionService = sysPositionService;
    }

    /**
     * 查询审批流程资源范围选项。
     *
     * @return 审批流程资源范围选项
     */
    public SystemFormOptionsView.ApprovalResourceScopeOptions approvalResourceScopeOptions() {
        Map<String, List<SystemFormOptionsView.CategoryOption>> categories = new LinkedHashMap<>();
        categories.put("AGENT", listAgentCategories());
        categories.put("WORKFLOW", listWorkflowCategories());
        categories.put("KNOWLEDGE", listKnowledgeCategories());
        return new SystemFormOptionsView.ApprovalResourceScopeOptions(
                categories,
                listDepartments()
        );
    }

    /**
     * 查询角色管理表单选项。
     *
     * @return 角色管理表单选项
     */
    public SystemFormOptionsView.RoleFormOptions roleFormOptions() {
        Map<String, List<SystemFormOptionsView.CategoryOption>> categories = new LinkedHashMap<>();
        categories.put("AGENT", listAgentCategories());
        categories.put("PLUGIN", listPluginCategories());
        categories.put("WORKFLOW", listWorkflowCategories());
        categories.put("KNOWLEDGE", listKnowledgeCategories());
        categories.put("RESOURCE", listResourceCategories());
        return new SystemFormOptionsView.RoleFormOptions(
                listMenus(),
                categories
        );
    }

    /**
     * 查询账号管理表单选项。
     *
     * @return 账号管理表单选项
     */
    public SystemFormOptionsView.AccountFormOptions accountFormOptions() {
        BigInteger tenantId = requireAccount().getTenantId();
        List<SystemFormOptionsView.RoleOption> roles = sysRoleService.list(QueryWrapper.create()
                        .eq(SysRole::getTenantId, tenantId)
                        .eq(SysRole::getStatus, EnumDataStatus.AVAILABLE.getCode())
                        .orderBy(SysRole::getId, true))
                .stream()
                .map(role -> new SystemFormOptionsView.RoleOption(role.getId(), role.getRoleName()))
                .toList();
        List<SystemFormOptionsView.PositionOption> positions = sysPositionService.list(QueryWrapper.create()
                        .eq(SysPosition::getTenantId, tenantId)
                        .eq(SysPosition::getStatus, EnumDataStatus.AVAILABLE.getCode())
                        .orderBy(SysPosition::getSortNo, true))
                .stream()
                .map(position -> new SystemFormOptionsView.PositionOption(
                        position.getId(), position.getPositionName()))
                .toList();
        return new SystemFormOptionsView.AccountFormOptions(
                listDepartments(),
                roles,
                positions
        );
    }

    /**
     * 校验账号表单引用的部门、角色和岗位。
     *
     * @param account 账号表单
     * @throws BusinessException 引用不存在或已禁用时抛出
     */
    public void validateAccountReferences(SysAccount account) {
        if (account == null) {
            throw new BusinessException("账号信息不能为空");
        }
        if (account.getDeptId() == null) {
            throw new BusinessException("部门不能为空");
        }
        SysDept dept = sysDeptService.getById(account.getDeptId());
        BigInteger tenantId = requireAccount().getTenantId();
        if (dept == null
                || !Objects.equals(dept.getTenantId(), tenantId)
                || !EnumDataStatus.AVAILABLE.getCode().equals(dept.getStatus())) {
            throw new BusinessException("部门不存在或已禁用");
        }
        assertAvailableIds(
                account.getRoleIds(),
                sysRoleService::listByIds,
                SysRole::getId,
                SysRole::getStatus,
                SysRole::getTenantId,
                tenantId,
                "角色"
        );
        if (account.getPositionIds() != null && !account.getPositionIds().isEmpty()) {
            assertAvailableIds(
                    account.getPositionIds(),
                    sysPositionService::listByIds,
                    SysPosition::getId,
                    SysPosition::getStatus,
                    SysPosition::getTenantId,
                    tenantId,
                    "岗位"
            );
        }
    }

    /**
     * 校验角色引用的菜单和部门。
     *
     * @param role 角色表单
     * @throws BusinessException 引用不存在时抛出
     */
    public void validateRoleReferences(SysRole role) {
        if (role == null) {
            throw new BusinessException("角色信息不能为空");
        }
        assertExistingIds(role.getMenuIds(), sysMenuService::listByIds, SysMenu::getId, "菜单");
        if (role.getDeptIds() != null && !role.getDeptIds().isEmpty()) {
            assertTenantIds(
                    role.getDeptIds(),
                    sysDeptService::listByIds,
                    SysDept::getId,
                    SysDept::getTenantId,
                    requireAccount().getTenantId(),
                    "部门");
        }
    }

    /**
     * 校验角色分类授权中提交的非 Bot 分类 ID。
     *
     * @param scopes 分类权限范围
     * @throws BusinessException 分类类型或分类 ID 非法时抛出
     */
    public void validateCategoryScopes(List<SysRoleCategoryScopeItemVo> scopes) {
        if (scopes == null) {
            return;
        }
        for (SysRoleCategoryScopeItemVo scope : scopes) {
            if (scope == null || scope.getCategoryIds() == null || scope.getCategoryIds().isEmpty()) {
                continue;
            }
            switch (String.valueOf(scope.getResourceType()).toUpperCase()) {
                case "AGENT" -> assertTenantIds(
                        scope.getCategoryIds(),
                        agentCategoryService::listByIds,
                        AgentCategory::getId,
                        AgentCategory::getTenantId,
                        requireAccount().getTenantId(),
                        "Agent 分类");
                case "PLUGIN" -> assertExistingIds(
                        scope.getCategoryIds(), pluginCategoryService::listByIds, PluginCategory::getId, "插件分类");
                case "WORKFLOW" -> assertExistingIds(
                        scope.getCategoryIds(), workflowCategoryService::listByIds, WorkflowCategory::getId, "工作流分类");
                case "KNOWLEDGE" -> assertExistingIds(
                        scope.getCategoryIds(),
                        documentCollectionCategoryService::listByIds,
                        DocumentCollectionCategory::getId,
                        "知识库分类");
                case "RESOURCE" -> assertExistingIds(
                        scope.getCategoryIds(), resourceCategoryService::listByIds, ResourceCategory::getId, "素材分类");
                case "BOT" -> throw new BusinessException("Bot 分类授权已停止维护");
                default -> throw new BusinessException("不支持的分类资源类型");
            }
        }
    }

    /**
     * 校验审批流程提交的分类和部门范围。
     *
     * @param resourceType 审批资源类型
     * @param scopes 审批范围
     * @throws BusinessException 范围引用不存在或资源类型不匹配时抛出
     */
    public void validateApprovalScopes(String resourceType, List<ApprovalFlowScopeVo> scopes) {
        if (scopes == null) {
            return;
        }
        for (ApprovalFlowScopeVo scope : scopes) {
            if (scope == null || scope.getScopeValue() == null) {
                continue;
            }
            String scopeType = String.valueOf(scope.getScopeType()).toUpperCase();
            if (ApprovalScopeType.DEPT.getCode().equals(scopeType)) {
                assertTenantIds(
                        List.of(scope.getScopeValue()),
                        sysDeptService::listByIds,
                        SysDept::getId,
                        SysDept::getTenantId,
                        requireAccount().getTenantId(),
                        "部门");
                continue;
            }
            if (!ApprovalScopeType.CATEGORY.getCode().equals(scopeType)) {
                throw new BusinessException("不支持的审批范围类型");
            }
            switch (String.valueOf(resourceType).toUpperCase()) {
                case "AGENT" -> assertTenantIds(
                        List.of(scope.getScopeValue()),
                        agentCategoryService::listByIds,
                        AgentCategory::getId,
                        AgentCategory::getTenantId,
                        requireAccount().getTenantId(),
                        "Agent 分类");
                case "WORKFLOW" -> assertExistingIds(
                        List.of(scope.getScopeValue()),
                        workflowCategoryService::listByIds,
                        WorkflowCategory::getId,
                        "工作流分类");
                case "KNOWLEDGE" -> assertExistingIds(
                        List.of(scope.getScopeValue()),
                        documentCollectionCategoryService::listByIds,
                        DocumentCollectionCategory::getId,
                        "知识库分类");
                default -> throw new BusinessException("当前资源类型不支持分类审批范围");
            }
        }
    }

    private List<SystemFormOptionsView.DepartmentOption> listDepartments() {
        BigInteger tenantId = requireAccount().getTenantId();
        List<SysDept> departments = sysDeptService.list(QueryWrapper.create()
                .eq(SysDept::getTenantId, tenantId)
                .eq(SysDept::getStatus, EnumDataStatus.AVAILABLE.getCode())
                .orderBy(SysDept::getSortNo, true));
        return buildDepartmentTree(departments);
    }

    private List<SystemFormOptionsView.MenuOption> listMenus() {
        List<SysMenu> menus = sysMenuService.list(QueryWrapper.create()
                .orderBy(SysMenu::getSortNo, true));
        return buildMenuTree(menus);
    }

    private List<SystemFormOptionsView.CategoryOption> listAgentCategories() {
        BigInteger tenantId = requireAccount().getTenantId();
        return agentCategoryService.list(QueryWrapper.create()
                        .eq(AgentCategory::getTenantId, tenantId)
                        .eq(AgentCategory::getStatus, EnumDataStatus.AVAILABLE.getCode())
                        .orderBy(AgentCategory::getSortNo, true))
                .stream()
                .map(category -> new SystemFormOptionsView.CategoryOption(
                        category.getId(), category.getCategoryName()))
                .toList();
    }

    private List<SystemFormOptionsView.CategoryOption> listWorkflowCategories() {
        return workflowCategoryService.list(QueryWrapper.create()
                        .orderBy(WorkflowCategory::getSortNo, true))
                .stream()
                .map(category -> new SystemFormOptionsView.CategoryOption(
                        category.getId(), category.getCategoryName()))
                .toList();
    }

    private List<SystemFormOptionsView.CategoryOption> listKnowledgeCategories() {
        return documentCollectionCategoryService.list(QueryWrapper.create()
                        .orderBy(DocumentCollectionCategory::getSortNo, true))
                .stream()
                .map(category -> new SystemFormOptionsView.CategoryOption(
                        category.getId(), category.getCategoryName()))
                .toList();
    }

    private List<SystemFormOptionsView.CategoryOption> listPluginCategories() {
        return pluginCategoryService.list(QueryWrapper.create()
                        .orderBy(PluginCategory::getId, true))
                .stream()
                .map(category -> new SystemFormOptionsView.CategoryOption(
                        category.getId(), category.getName()))
                .toList();
    }

    private List<SystemFormOptionsView.CategoryOption> listResourceCategories() {
        return resourceCategoryService.list(QueryWrapper.create()
                        .orderBy(ResourceCategory::getSortNo, true))
                .stream()
                .map(category -> new SystemFormOptionsView.CategoryOption(
                        category.getId(), category.getCategoryName()))
                .toList();
    }

    private List<SystemFormOptionsView.DepartmentOption> buildDepartmentTree(List<SysDept> departments) {
        Set<BigInteger> ids = new HashSet<>();
        departments.forEach(item -> ids.add(item.getId()));
        Map<BigInteger, List<SysDept>> children = new LinkedHashMap<>();
        List<SysDept> roots = new ArrayList<>();
        for (SysDept department : departments) {
            BigInteger parentId = department.getParentId();
            if (parentId == null || BigInteger.ZERO.equals(parentId) || !ids.contains(parentId)) {
                roots.add(department);
            } else {
                children.computeIfAbsent(parentId, ignored -> new ArrayList<>()).add(department);
            }
        }
        return roots.stream().map(item -> toDepartmentOption(item, children)).toList();
    }

    private SystemFormOptionsView.DepartmentOption toDepartmentOption(
            SysDept department,
            Map<BigInteger, List<SysDept>> children) {
        return new SystemFormOptionsView.DepartmentOption(
                department.getId(),
                department.getParentId(),
                department.getDeptName(),
                children.getOrDefault(department.getId(), List.of()).stream()
                        .map(item -> toDepartmentOption(item, children))
                        .toList()
        );
    }

    private List<SystemFormOptionsView.MenuOption> buildMenuTree(List<SysMenu> menus) {
        Set<BigInteger> ids = new HashSet<>();
        menus.forEach(item -> ids.add(item.getId()));
        Map<BigInteger, List<SysMenu>> children = new LinkedHashMap<>();
        List<SysMenu> roots = new ArrayList<>();
        for (SysMenu menu : menus) {
            BigInteger parentId = menu.getParentId();
            if (parentId == null || BigInteger.ZERO.equals(parentId) || !ids.contains(parentId)) {
                roots.add(menu);
            } else {
                children.computeIfAbsent(parentId, ignored -> new ArrayList<>()).add(menu);
            }
        }
        return roots.stream().map(item -> toMenuOption(item, children)).toList();
    }

    private SystemFormOptionsView.MenuOption toMenuOption(
            SysMenu menu,
            Map<BigInteger, List<SysMenu>> children) {
        return new SystemFormOptionsView.MenuOption(
                menu.getId(),
                menu.getParentId(),
                menu.getMenuTitle(),
                children.getOrDefault(menu.getId(), List.of()).stream()
                        .map(item -> toMenuOption(item, children))
                        .toList()
        );
    }

    private <T> void assertAvailableIds(
            Collection<BigInteger> rawIds,
            Function<Collection<BigInteger>, List<T>> loader,
            Function<T, BigInteger> idGetter,
            Function<T, Integer> statusGetter,
            Function<T, BigInteger> tenantGetter,
            BigInteger tenantId,
            String label) {
        Set<BigInteger> ids = normalizeIds(rawIds);
        if (ids.isEmpty()) {
            throw new BusinessException(label + "不能为空");
        }
        List<T> records = loader.apply(ids);
        boolean valid = records.size() == ids.size()
                && records.stream().allMatch(item ->
                ids.contains(idGetter.apply(item))
                        && EnumDataStatus.AVAILABLE.getCode().equals(statusGetter.apply(item))
                        && Objects.equals(tenantGetter.apply(item), tenantId));
        if (!valid) {
            throw new BusinessException(label + "不存在或已禁用");
        }
    }

    private <T> void assertExistingIds(
            Collection<BigInteger> rawIds,
            Function<Collection<BigInteger>, List<T>> loader,
            Function<T, BigInteger> idGetter,
            String label) {
        Set<BigInteger> ids = normalizeIds(rawIds);
        if (ids.isEmpty()) {
            return;
        }
        List<T> records = loader.apply(ids);
        boolean valid = records.size() == ids.size()
                && records.stream().allMatch(item -> ids.contains(idGetter.apply(item)));
        if (!valid) {
            throw new BusinessException(label + "不存在或无权访问");
        }
    }

    private <T> void assertTenantIds(
            Collection<BigInteger> rawIds,
            Function<Collection<BigInteger>, List<T>> loader,
            Function<T, BigInteger> idGetter,
            Function<T, BigInteger> tenantGetter,
            BigInteger tenantId,
            String label) {
        Set<BigInteger> ids = normalizeIds(rawIds);
        if (ids.isEmpty()) {
            return;
        }
        List<T> records = loader.apply(ids);
        boolean valid = records.size() == ids.size()
                && records.stream().allMatch(item ->
                ids.contains(idGetter.apply(item))
                        && Objects.equals(tenantGetter.apply(item), tenantId));
        if (!valid) {
            throw new BusinessException(label + "不存在或无权访问");
        }
    }

    private LoginAccount requireAccount() {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        if (account == null || account.getTenantId() == null) {
            throw new BusinessException("当前登录状态失效，请重新登录后再试");
        }
        return account;
    }

    private Set<BigInteger> normalizeIds(Collection<BigInteger> rawIds) {
        Set<BigInteger> ids = new LinkedHashSet<>();
        if (rawIds != null) {
            rawIds.stream().filter(Objects::nonNull).forEach(ids::add);
        }
        return ids;
    }
}
