package tech.easyflow.admin.controller.skill;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.mybatisflex.core.query.QueryWrapper;
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.common.annotation.UsePermission;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.util.SearchKeywordUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.skill.entity.SkillCategory;
import tech.easyflow.skill.service.SkillCategoryService;
import tech.easyflow.system.entity.vo.RoleCategoryAccessSnapshot;
import tech.easyflow.system.enums.CategoryResourceType;
import tech.easyflow.system.service.CategoryPermissionService;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
 * Skill 分类管理控制器。
 */
@RestController
@RequestMapping("/api/v1/skill/category")
@UsePermission(moduleName = "/api/v1/skill")
public class SkillCategoryController {

    private static final Set<String> SORT_COLUMNS = Set.of(
            "id", "category_name", "parent_id", "level_no", "sort_no", "status", "created", "modified");

    private final SkillCategoryService service;
    @javax.annotation.Resource
    private CategoryPermissionService categoryPermissionService;

    /**
     * 创建 Skill 分类管理控制器。
     *
     * @param service Skill 分类服务
     */
    public SkillCategoryController(SkillCategoryService service) {
        this.service = service;
    }

    /**
     * 查询当前用户可见的 Skill 分类。
     *
     * @param entity 查询条件
     * @param asTree 是否转树
     * @param sortKey 排序字段
     * @param sortType 排序方式
     * @return 可见分类列表
     */
    @GetMapping("visibleList")
    @SaCheckPermission("/api/v1/skill/query")
    public Result<List<SkillCategory>> visibleList(SkillCategory entity, Boolean asTree, String sortKey, String sortType) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(SkillCategory::getTenantId, currentAccount().getTenantId());
        if (entity != null) {
            queryWrapper.eq(SkillCategory::getId, entity.getId(), entity.getId() != null)
                    .eq(SkillCategory::getParentId, entity.getParentId(), entity.getParentId() != null)
                    .eq(SkillCategory::getLevelNo, entity.getLevelNo(), entity.getLevelNo() != null)
                    .eq(SkillCategory::getStatus, entity.getStatus(), entity.getStatus() != null);
            if (entity.getCategoryName() != null && !entity.getCategoryName().isBlank()) {
                queryWrapper.and("category_name LIKE ?",
                        SearchKeywordUtil.literalContainsPattern(entity.getCategoryName()));
            }
        }
        RoleCategoryAccessSnapshot access = categoryPermissionService.getCurrentAccess(CategoryResourceType.SKILL.getCode());
        queryWrapper.orderBy(resolveOrderBy(sortKey, sortType));
        List<SkillCategory> categories = service.list(queryWrapper);
        if (access.isRestricted()) {
            Set<BigInteger> visibleIds = new java.util.LinkedHashSet<>(access.getCategoryIds());
            categories.stream().filter(category -> access.getCategoryIds().contains(category.getId()))
                    .map(SkillCategory::getAncestors).filter(value -> value != null && !value.isBlank())
                    .flatMap(value -> java.util.Arrays.stream(value.split(",")))
                    .map(String::trim).filter(value -> !value.isBlank() && !"0".equals(value))
                    .map(BigInteger::new).forEach(visibleIds::add);
            categories = categories.stream().filter(category -> visibleIds.contains(category.getId())).toList();
        }
        return Result.ok(Boolean.FALSE.equals(asTree) ? categories : toTree(categories));
    }

    /**
     * 查询当前租户完整分类管理树，包含停用分类。
     *
     * @return 分类树
     */
    @GetMapping("tree")
    @SaCheckPermission("/api/v1/skill/category")
    public Result<List<SkillCategory>> tree() {
        List<SkillCategory> categories = service.list(QueryWrapper.create()
                .eq(SkillCategory::getTenantId, currentAccount().getTenantId())
                .orderBy("sort_no asc, id asc"));
        return Result.ok(toTree(categories));
    }

    /**
     * 移动 Skill 分类到新的父级。
     *
     * @param id 分类 ID
     * @param parentId 新父级 ID，根分类为空
     * @return 更新结果
     */
    @PostMapping("move")
    @SaCheckPermission("/api/v1/skill/category")
    public Result<?> move(
            @JsonBody(value = "id", required = true, skipConvertError = false) BigInteger id,
            @JsonBody(value = "parentId", skipConvertError = false) BigInteger parentId) {
        SkillCategory category = service.getOne(QueryWrapper.create()
                .eq(SkillCategory::getId, id)
                .eq(SkillCategory::getTenantId, currentAccount().getTenantId()));
        if (category == null) {
            throw new BusinessException(404, 404, "Skill 分类不存在");
        }
        category.setParentId(parentId);
        if (!service.updateById(category)) {
            throw new BusinessException(500, 500, "移动 Skill 分类失败，请稍后重试");
        }
        return Result.ok();
    }

    /**
     * 创建 Skill 分类。
     *
     * @param entity 分类
     * @return 保存结果
     */
    @PostMapping("save")
    @SaCheckPermission("/api/v1/skill/category")
    public Result<?> save(@JsonBody(required = true, skipConvertError = false) SkillCategory entity) {
        if (entity != null) {
            entity.setId(null);
            entity.setTenantId(null);
            entity.setAncestors(null);
            entity.setLevelNo(null);
            entity.setCreated(null);
            entity.setCreatedBy(null);
            entity.setModified(null);
            entity.setModifiedBy(null);
        }
        if (!service.save(entity)) {
            throw new BusinessException(500, 500, "创建 Skill 分类失败，请稍后重试");
        }
        return Result.ok(entity);
    }

    /**
     * 更新 Skill 分类。
     *
     * @param entity 分类
     * @return 更新结果
     */
    @PostMapping("update")
    @SaCheckPermission("/api/v1/skill/category")
    public Result<?> update(@JsonBody(required = true, skipConvertError = false) SkillCategory entity) {
        if (entity != null) {
            entity.setTenantId(null);
            entity.setAncestors(null);
            entity.setLevelNo(null);
            entity.setCreated(null);
            entity.setCreatedBy(null);
            entity.setModified(null);
            entity.setModifiedBy(null);
        }
        if (entity == null || entity.getId() == null) {
            throw new BusinessException("Skill 分类 ID 不能为空");
        }
        if (!service.updateById(entity)) {
            throw new BusinessException(500, 500, "更新 Skill 分类失败，请稍后重试");
        }
        return Result.ok(entity);
    }

    /**
     * 删除 Skill 分类。
     *
     * @param id 分类 ID
     * @return 删除结果
     */
    @PostMapping("remove")
    @SaCheckPermission("/api/v1/skill/category")
    public Result<?> remove(
            @JsonBody(value = "id", required = true, skipConvertError = false) Serializable id) {
        if (!service.removeById(id)) {
            throw new BusinessException(500, 500, "删除 Skill 分类失败，请稍后重试");
        }
        return Result.ok();
    }

    private LoginAccount currentAccount() {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        if (account == null || account.getId() == null || account.getTenantId() == null) {
            throw new BusinessException(401, 401, "未登录或登录态无效");
        }
        return account;
    }

    /**
     * 将分类排序参数收敛到固定字段白名单，禁止原始 SQL 片段进入查询。
     *
     * @param sortKey 排序字段
     * @param sortType 排序方向
     * @return 安全排序表达式
     */
    String resolveOrderBy(String sortKey, String sortType) {
        String snake = sortKey == null ? "" : sortKey
                .replaceAll("([a-z0-9])([A-Z])", "$1_$2")
                .toLowerCase(Locale.ROOT);
        String column = SORT_COLUMNS.contains(snake) ? snake : "sort_no";
        String direction = "desc".equalsIgnoreCase(sortType) ? "desc" : "asc";
        return column + " " + direction + ("id".equals(column) ? "" : ", id asc");
    }

    private List<SkillCategory> toTree(List<SkillCategory> categories) {
        Map<java.math.BigInteger, SkillCategory> byId = new LinkedHashMap<>();
        categories.forEach(category -> {
            category.setChildren(null);
            byId.put(category.getId(), category);
        });
        List<SkillCategory> roots = new java.util.ArrayList<>();
        for (SkillCategory category : categories) {
            SkillCategory parent = category.getParentId() == null ? null : byId.get(category.getParentId());
            if (parent == null) {
                roots.add(category);
            } else {
                parent.getChildren().add(category);
            }
        }
        return roots;
    }
}
