package com.agileboot.system.dept.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeUtil;
import com.agileboot.common.core.enums.BasicEnumUtil;
import com.agileboot.common.core.enums.common.StatusEnum;
import com.agileboot.common.core.exception.BizException;
import com.agileboot.common.core.exception.error.ErrorCode;
import com.agileboot.system.dept.dto.AddDeptDTO;
import com.agileboot.system.dept.dto.DeptQuery;
import com.agileboot.system.dept.dto.UpdateDeptDTO;
import com.agileboot.system.dept.entity.SysDept;
import com.agileboot.system.dept.mapper.SysDeptMapper;
import com.agileboot.system.dept.service.ISysDeptService;
import com.agileboot.system.dept.vo.DeptVO;
import com.agileboot.system.user.service.ISysUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements ISysDeptService {

    private final ISysUserService userService;

    @Override
    public List<DeptVO> getDeptList(DeptQuery query) {
        List<SysDept> list = super.list(query.toQueryWrapper());
        return list.stream().map(DeptVO::new).collect(Collectors.toList());
    }

    @Override
    public DeptVO getDeptInfo(Long deptId) {
        SysDept one = super.getById(deptId);
        return new DeptVO(one);
    }

    @Override
    public List<Tree<Long>> getDeptTree() {
        List<SysDept> list = super.list();
        return TreeUtil.build(list, 0L, (dept, tree) -> {
            tree.setId(dept.getDeptId());
            tree.setParentId(dept.getParentId());
            tree.putExtra("label", dept.getDeptName());
        });
    }

    @Override
    public void addDept(AddDeptDTO addDTO) {
        Long parentId = addDTO.getParentId();
        SysDept entity = new SysDept();
        entity.setParentId(parentId);
        entity.setDeptName(addDTO.getDeptName());
        entity.setOrderNum(addDTO.getOrderNum());
        entity.setLeaderName(addDTO.getLeaderName());
        entity.setPhone(addDTO.getPhone());
        entity.setEmail(addDTO.getEmail());
        entity.setStatus(addDTO.getStatus());

        LambdaQueryWrapper<SysDept> queryWrapper = Wrappers.lambdaQuery(SysDept.class)
                .eq(SysDept::getDeptName, addDTO.getDeptName())
                .eq(parentId != null, SysDept::getDeptId, parentId);
        if (super.exists(queryWrapper)) {
            throw new BizException(ErrorCode.Business.DEPT_NAME_IS_NOT_UNIQUE.message(), addDTO.getDeptName());
        }
        SysDept sysDept = generateAncestors(entity);
        super.save(sysDept);
    }

    @Override
    public void updateDept(UpdateDeptDTO updateDTO) {
        Long deptId = updateDTO.getDeptId();
        Long parentId = updateDTO.getParentId();
        if (Objects.equals(parentId, deptId)) {
            throw new BizException(ErrorCode.Business.DEPT_PARENT_ID_IS_NOT_ALLOWED_SELF);
        }
        Integer status = updateDTO.getStatus();
        SysDept entity = super.getById(deptId);
        if (entity == null) {
            throw new BizException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND.message(), deptId, "部门");
        }
        entity.setParentId(parentId);
        entity.setDeptName(updateDTO.getDeptName());
        entity.setOrderNum(updateDTO.getOrderNum());
        entity.setLeaderName(updateDTO.getLeaderName());
        entity.setPhone(updateDTO.getPhone());
        entity.setEmail(updateDTO.getEmail());
        entity.setStatus(Convert.toInt(status, 0));

        LambdaQueryWrapper<SysDept> queryWrapper = Wrappers.lambdaQuery(SysDept.class)
                .eq(SysDept::getDeptName, updateDTO.getDeptName())
                .ne(deptId != null, SysDept::getDeptId, deptId)
                .eq(parentId != null, SysDept::getDeptId, parentId);
        if (super.exists(queryWrapper)) {
            throw new BizException(ErrorCode.Business.DEPT_NAME_IS_NOT_UNIQUE.message(), updateDTO.getDeptName());
        }
        if (StatusEnum.DISABLE.getValue().equals(entity.getStatus()) && this.hasChildrenDept(deptId, true)) {
            throw new BizException(ErrorCode.Business.DEPT_STATUS_ID_IS_NOT_ALLOWED_CHANGE);
        }
        SysDept sysDept = generateAncestors(entity);
        super.updateById(sysDept);
    }

    @Override
    public void removeDept(Long deptId) {
        SysDept entity = super.getById(deptId);
        if (entity == null) {
            throw new BizException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND.message(), deptId, "部门");
        }
        if (this.hasChildrenDept(entity.getDeptId(), null)) {
            throw new BizException(ErrorCode.Business.DEPT_EXIST_CHILD_DEPT_NOT_ALLOW_DELETE);
        }
        userService.checkDeptAssignedToUsers(entity.getDeptId());

        super.removeById(deptId);
    }

    private SysDept generateAncestors(SysDept entity) {
        Long parentId = entity.getParentId();
        if (parentId == null || parentId == 0) {
            entity.setAncestors(String.valueOf(parentId == null ? 0 : parentId));
        } else {
            SysDept parentDept = super.getById(parentId);
            // 检查 parentDept 是否为 null 或者状态为禁用
            if (parentDept == null || StatusEnum.DISABLE.equals(
                    BasicEnumUtil.fromValue(StatusEnum.class, parentDept.getStatus()))) {
                throw new BizException(ErrorCode.Business.DEPT_PARENT_DEPT_NO_EXIST_OR_DISABLED);
            }
            // 处理 parentDept.getAncestors() 可能为 null 的情况
            String ancestors = parentDept.getAncestors() == null ? "" : parentDept.getAncestors();
            entity.setAncestors(ancestors + "," + parentId);
        }
        return entity;
    }

    private boolean hasChildrenDept(Long deptId, Boolean enabled) {
        LambdaQueryWrapper<SysDept> queryWrapper = Wrappers.lambdaQuery(SysDept.class)
                .eq(enabled != null, SysDept::getStatus, 1)
                .and(o -> o.eq(SysDept::getParentId, deptId).or()
                        .apply("FIND_IN_SET (" + deptId + " , ancestors)")
                );
        return super.exists(queryWrapper);
    }
}
