package tech.easyflow.admin.controller.system;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.approval.entity.ApprovalFlowScope;
import tech.easyflow.approval.entity.ApprovalFlowStepAssignee;
import tech.easyflow.approval.enums.ApprovalAssigneeType;
import tech.easyflow.approval.enums.ApprovalScopeType;
import tech.easyflow.approval.mapper.ApprovalFlowScopeMapper;
import tech.easyflow.approval.mapper.ApprovalFlowStepAssigneeMapper;
import tech.easyflow.common.constant.Constants;
import tech.easyflow.common.constant.enums.EnumDataStatus;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.tree.Tree;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.system.entity.SysAccount;
import tech.easyflow.system.entity.SysDept;
import tech.easyflow.system.service.SysAccountService;
import tech.easyflow.system.service.SysDeptService;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * 部门表 控制层。
 *
 * @author ArkLight
 * @since 2025-03-14
 */
@RestController("sysDeptController")
@RequestMapping("/api/v1/sysDept")
public class SysDeptController extends BaseCurdController<SysDeptService, SysDept> {

    private final SysAccountService sysAccountService;
    private final ApprovalFlowStepAssigneeMapper approvalFlowStepAssigneeMapper;
    private final ApprovalFlowScopeMapper approvalFlowScopeMapper;

    /**
     * 创建部门管理控制器。
     *
     * @param service 部门服务
     * @param sysAccountService 用户服务
     * @param approvalFlowStepAssigneeMapper 审批步骤对象 Mapper
     * @param approvalFlowScopeMapper 审批范围 Mapper
     */
    public SysDeptController(SysDeptService service,
                             SysAccountService sysAccountService,
                             ApprovalFlowStepAssigneeMapper approvalFlowStepAssigneeMapper,
                             ApprovalFlowScopeMapper approvalFlowScopeMapper) {
        super(service);
        this.sysAccountService = sysAccountService;
        this.approvalFlowStepAssigneeMapper = approvalFlowStepAssigneeMapper;
        this.approvalFlowScopeMapper = approvalFlowScopeMapper;
    }

    /**
     * 获取部门列表默认排序规则。
     *
     * @return 默认排序表达式
     */
    @Override
    protected String getDefaultOrderBy() {
        return "sort_no asc";
    }

    /**
     * 查询部门列表并组装为树形结构。
     *
     * @param entity 查询条件
     * @param asTree 是否返回树形结构
     * @param sortKey 排序字段
     * @param sortType 排序方向
     * @return 部门树
     */
    @Override
    @GetMapping("list")
    public Result<List<SysDept>> list(SysDept entity, Boolean asTree, String sortKey, String sortType) {
        QueryWrapper queryWrapper = QueryWrapper.create(entity, buildOperators(entity));
        String keyword = entity == null ? "" : normalizeSearchKeyword(entity.getKeyword());
        if (tech.easyflow.common.util.StringUtil.hasText(keyword)) {
            queryWrapper.and(buildLiteralContainsCondition(
                    keyword, new QueryColumn("dept_name"), new QueryColumn("dept_code")));
        }
        queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
        List<SysDept> matchedDepartments = service.list(queryWrapper);
        if (!tech.easyflow.common.util.StringUtil.hasText(keyword)) {
            return Result.ok(Tree.tryToTree(matchedDepartments, "id", "parentId"));
        }
        if (matchedDepartments.isEmpty()) {
            return Result.ok(List.of());
        }

        // 搜索结果保留所有重名命中项，并补齐各自祖先节点以维持可定位的树结构。
        Set<BigInteger> visibleIds = new LinkedHashSet<>();
        for (SysDept department : matchedDepartments) {
            visibleIds.add(department.getId());
            addAncestorIds(visibleIds, department.getAncestors());
        }
        QueryWrapper visibleDepartmentQuery = QueryWrapper.create()
                .in(SysDept::getId, visibleIds)
                .orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
        List<SysDept> visibleDepartments = service.list(visibleDepartmentQuery);
        return Result.ok(Tree.tryToTree(visibleDepartments, "id", "parentId"));
    }

    /**
     * 将逗号分隔的祖先 ID 加入可见集合。
     *
     * @param visibleIds 可见部门 ID 集合
     * @param ancestors 祖先路径
     */
    private void addAncestorIds(Set<BigInteger> visibleIds, String ancestors) {
        if (!tech.easyflow.common.util.StringUtil.hasText(ancestors)) {
            return;
        }
        for (String ancestor : ancestors.split(",")) {
            String normalized = ancestor.trim();
            if (!normalized.isEmpty() && !"0".equals(normalized)) {
                visibleIds.add(new BigInteger(normalized));
            }
        }
    }

    /**
     * 批量修改部门状态。
     *
     * @param ids 部门主键集合
     * @param status 目标状态
     * @return 实际更新数量
     */
    @PostMapping("changeStatusBatch")
    @SaCheckPermission("/api/v1/sysDept/save")
    @Transactional(rollbackFor = Exception.class)
    public Result<Integer> changeStatusBatch(
            @JsonBody(value = "ids", required = true) List<BigInteger> ids,
            @JsonBody(value = "status", required = true) Integer status) {
        Set<BigInteger> uniqueIds = normalizeIds(ids);
        if (uniqueIds.isEmpty()) {
            return Result.fail("请选择需要操作的部门", null);
        }
        if (!EnumDataStatus.AVAILABLE.getCode().equals(status)
                && !EnumDataStatus.UNAVAILABLE.getCode().equals(status)) {
            return Result.fail("部门状态不合法", null);
        }

        List<SysDept> records = service.listByIds(uniqueIds);
        if (records.size() != uniqueIds.size()) {
            return Result.fail("部分部门不存在或已删除，请刷新后重试", null);
        }
        if (EnumDataStatus.UNAVAILABLE.getCode().equals(status) && containsRootDept(records)) {
            return Result.fail("根部门不能禁用", null);
        }
        if (EnumDataStatus.UNAVAILABLE.getCode().equals(status)
                && isUsedByApprovalFlow(uniqueIds)) {
            return Result.fail("所选部门已被审批流程使用，不能禁用", null);
        }

        LoginAccount loginUser = SaTokenUtil.getLoginAccount();
        SysDept update = new SysDept();
        update.setStatus(status);
        update.setModified(new Date());
        update.setModifiedBy(loginUser.getId());

        QueryWrapper updateWrapper = QueryWrapper.create();
        updateWrapper.in(SysDept::getId, uniqueIds);
        int updated = service.getMapper().updateByQuery(update, updateWrapper);
        if (updated <= 0) {
            return Result.fail("部门状态修改失败", null);
        }
        return Result.ok(updated);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Result onSaveOrUpdateBefore(SysDept entity, boolean isSave) {
        LoginAccount loginUser = SaTokenUtil.getLoginAccount();
        if (isSave && entity.getStatus() == null) {
            entity.setStatus(EnumDataStatus.AVAILABLE.getCode());
        }
        BigInteger parentId = entity.getParentId();
        if (parentId.equals(BigInteger.ZERO)) {
            entity.setAncestors(parentId.toString());
        } else {
            SysDept parent = service.getById(parentId);
            entity.setAncestors(parent.getAncestors() + "," + parentId);
        }
        if (isSave) {
            commonFiled(entity, loginUser.getId(), loginUser.getTenantId(), loginUser.getDeptId());
        } else {
            entity.setModified(new Date());
            entity.setModifiedBy(loginUser.getId());
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Result onRemoveBefore(Collection<Serializable> ids) {
        List<SysDept> records = service.listByIds(ids);
        if (records.size() != ids.size()) {
            return Result.fail(1, "部分部门不存在或已删除，请刷新后重试");
        }
        if (containsRootDept(records)) {
            return Result.fail(1, "无法删除根部门");
        }

        QueryWrapper childQuery = QueryWrapper.create();
        childQuery.in(SysDept::getParentId, ids);
        childQuery.notIn(SysDept::getId, ids);
        if (service.count(childQuery) > 0) {
            return Result.fail(1, "所选部门包含未选中的下级部门，不能删除");
        }
        if (isUsedByApprovalFlow(ids)) {
            return Result.fail(1, "所选部门已被审批流程使用，请先调整审批配置");
        }

        QueryWrapper accountQuery = QueryWrapper.create();
        accountQuery.in(SysAccount::getDeptId, ids);
        long count = sysAccountService.count(accountQuery);
        if (count > 0) {
            return Result.fail(1, "所选部门下有员工，不能删除");
        }
        return super.onRemoveBefore(ids);
    }

    /**
     * 去重并过滤无效部门主键。
     *
     * @param ids 原始部门主键集合
     * @return 有效且去重后的主键集合
     */
    private Set<BigInteger> normalizeIds(Collection<BigInteger> ids) {
        Set<BigInteger> uniqueIds = new LinkedHashSet<>();
        if (ids == null) {
            return uniqueIds;
        }
        for (BigInteger id : ids) {
            if (id != null) {
                uniqueIds.add(id);
            }
        }
        return uniqueIds;
    }

    /**
     * 判断部门集合中是否包含根部门。
     *
     * @param records 部门集合
     * @return 包含根部门时返回 {@code true}
     */
    private boolean containsRootDept(Collection<SysDept> records) {
        return records.stream()
                .anyMatch(dept -> Constants.ROOT_DEPT.equals(dept.getDeptCode()));
    }

    /**
     * 判断部门是否仍被审批步骤或审批范围引用。
     *
     * @param ids 部门主键集合
     * @return 存在审批流程引用时返回 {@code true}
     */
    private boolean isUsedByApprovalFlow(Collection<? extends Serializable> ids) {
        QueryWrapper assigneeQuery = QueryWrapper.create();
        assigneeQuery.eq(
                ApprovalFlowStepAssignee::getAssigneeType,
                ApprovalAssigneeType.DEPT.getCode());
        assigneeQuery.in(ApprovalFlowStepAssignee::getTargetId, ids);
        if (approvalFlowStepAssigneeMapper.selectCountByQuery(assigneeQuery) > 0) {
            return true;
        }

        QueryWrapper scopeQuery = QueryWrapper.create();
        scopeQuery.eq(ApprovalFlowScope::getScopeType, ApprovalScopeType.DEPT.getCode());
        scopeQuery.in(ApprovalFlowScope::getScopeValue, ids);
        return approvalFlowScopeMapper.selectCountByQuery(scopeQuery) > 0;
    }
}
