/*
 * Copyright 2017-2025 noear.org and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.noear.solon.ai.skills.cli;

import org.noear.solon.ai.util.Markdown;
import org.noear.solon.ai.util.MarkdownUtil;
import org.noear.solon.core.util.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 资源池管理（主要是技能池）
 *
 * @author noear
 * @since 3.9.5
 */
public class PoolManager {
    private static final Logger LOG = LoggerFactory.getLogger(PoolManager.class);

    // 逻辑路径前缀 -> 物理根路径 (如 "@shared" -> "/path/to/shared")
    private final Map<String, Path> poolMap = new ConcurrentHashMap<>();

    // 逻辑全路径 -> 技能目录信息 (如 "@shared/video-creator" -> SkillDir)
    private final Map<String, SkillDir> skillMap = new ConcurrentHashMap<>();

    /**
     * 注册池（并扫描）
     */
    public PoolManager register(String alias, Path dir) {
        Path rootPath = dir.toAbsolutePath().normalize();
        String key = alias.startsWith("@") ? alias : "@" + alias;

        if (Files.exists(rootPath) && Files.isDirectory(rootPath)) {
            poolMap.put(key, rootPath);
            scanAndCache(key, rootPath);
            LOG.debug("Skill pool has been loaded.: {} -> {}", key, rootPath);
        } else {
            String reason = !Files.exists(rootPath) ? "The path does not exist." : "Not an effective directory";
            LOG.debug("Skill pool loading skip：{} (alias: {}, path: {})", reason, key, rootPath);
        }

        return this;
    }

    /**
     * 注册池（并扫描）
     */
    public PoolManager register(String alias, String dir) {
        return register(alias, Paths.get(dir));
    }

    /**
     * 刷新池（重新扫描所有池）
     */
    public void refresh() {
        skillMap.clear();
        poolMap.forEach(this::scanAndCache);
    }

    /**
     * 将逻辑路径解析为物理路径
     */
    public Path resolve(Path workDir, String pStr) {
        if (pStr == null || pStr.isEmpty() || ".".equals(pStr)) return workDir;

        if (pStr.startsWith("@")) {
            for (Map.Entry<String, Path> e : poolMap.entrySet()) {
                if (pStr.startsWith(e.getKey())) {
                    String sub = pStr.substring(e.getKey().length()).replaceFirst("^[/\\\\]", "");
                    return e.getValue().resolve(sub).normalize();
                }
            }
        }

        String cleanPath = pStr.startsWith("./") ? pStr.substring(2) : pStr;
        return workDir.resolve(cleanPath).normalize();
    }

    public Map<String, SkillDir> getSkillMap() {
        return skillMap;
    }

    public SkillDir getSkill(String aliasPath) {
        return skillMap.get(aliasPath);
    }

    public Map<String, Path> getPoolMap() {
        return poolMap;
    }

    private boolean isSkillDir(Path p) {
        return Files.exists(p.resolve("SKILL.md")) || Files.exists(p.resolve("skill.md"));
    }


    private void scanAndCache(String alias, Path root) {
        try {
            Files.walkFileTree(root, EnumSet.noneOf(FileVisitOption.class), 3, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    if (isSkillDir(dir)) {
                        String name = root.relativize(dir).toString().replace("\\", "/");
                        String aliasPath = alias + (name.isEmpty() ? "" : "/" + name);
                        skillMap.put(aliasPath, new SkillDir(name, aliasPath, dir, parseDescription(dir)));
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                    if (dir.getFileName().toString().startsWith(".")) return FileVisitResult.SKIP_SUBTREE;
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            LOG.error("Scan skill pool failed: {}", root, e);
        }
    }

    private String parseDescription(Path dir) {
        Path md = dir.resolve("SKILL.md");
        if (!Files.exists(md)) {
            md = dir.resolve("skill.md");
        }

        try {
            List<String> lines = Files.readAllLines(md, StandardCharsets.UTF_8);
            Markdown markdown = MarkdownUtil.resolve(lines, true);

            String desc = markdown.getDescription();

            if (Assert.isEmpty(desc)) {
                return "技能规约。";
            } else {
                // 增加长度到 150，确保 LLM 能看到足够的语义信息
                return desc.length() > 150 ? desc.substring(0, 147) + "..." : desc;
            }
        } catch (Throwable e) {
            return "技能规约。";
        }
    }
}