import * as fs from "node:fs/promises";
import * as path from "node:path";
import { debug } from "./core/logger.js";
import { safeJsonParse } from "./core/helpers.js";
import { isPathInScope } from "./core/path_scope_utils.js";
/**
 * 新人引导模块。
 * @description 为新加入项目的开发者提供分步引导流程，包含项目概览、代码导览、知识库复习和首个任务建议四个阶段。
 *              支持断点续传，引导进度持久化到 .soloforge/state/onboarding-session.json。
 */
// ── 常量定义 ──
/** 引导流程全部步骤，按顺序执行 */
const ALL_STEPS = ["project_overview", "code_tour", "knowledge_review", "first_task"];
/** 关键目录名称模式，用于在代码导览阶段识别重要模块 */
const KEY_DIRECTORY_PATTERNS = [
    "controller", "controllers",
    "service", "services",
    "component", "components",
    "page", "pages",
    "route", "routes", "router", "routers",
    "handler", "handlers",
    "model", "models",
];
/** 入口文件匹配模式，用于识别项目启动入口 */
const ENTRY_POINT_PATTERNS = [
    /^index\.\w+$/,
    /^main\.\w+$/,
    /^app\.\w+$/,
    /^server\.\w+$/,
];
/** 各步骤的中文描述，用于生成下一步操作提示 */
const STEP_DESCRIPTIONS = {
    project_overview: "项目概览",
    code_tour: "代码游览",
    knowledge_review: "知识库复习",
    first_task: "首次任务",
};
// ── 主函数 ──
/**
 * 执行新人引导流程的内部主函数。
 * @description 根据已完成步骤自动推进到下一个未完成的引导阶段，执行对应逻辑并更新进度。
 *              支持断点续传：如果所有步骤都已完成，直接返回最终状态。
 * @param input - 引导流程输入参数
 * @returns 引导执行结果，包含当前步骤、已完成步骤和下一步操作提示
 */
export async function runOnboarding(input) {
    debug("引导模块", "开始执行引导流程");
    const { config, projectPath, gitOps, knowledgeIndex } = input;
    const completedSteps = [...(input.completedSteps ?? [])];
    // 确定要执行的引导步骤
    let currentStep;
    if (input.currentStep && !completedSteps.includes(input.currentStep)) {
        // 优先使用调用者指定的步骤（前提是未完成）
        debug("引导模块", "使用调用者指定步骤 " + input.currentStep);
        currentStep = input.currentStep;
    }
    else {
        // 自动推进到第一个未完成的步骤
        debug("引导模块", "自动推进到下一个未完成步骤，已完成=" + (input.completedSteps ?? []).length);
        const nextStep = ALL_STEPS.find((s) => !completedSteps.includes(s));
        if (nextStep) {
            currentStep = nextStep;
        }
        else {
            // 所有步骤已完成，返回最终状态
            debug("引导模块", "所有引导步骤已完成");
            return {
                current_step: "first_task",
                completed_steps: [...ALL_STEPS],
                next_action_hint: "引导已完成！您可以开始使用 SoloForge。",
            };
        }
    }
    const result = {
        current_step: currentStep,
        completed_steps: [...completedSteps],
        next_action_hint: "",
    };
    // 执行当前步骤
    switch (currentStep) {
        case "project_overview":
            debug("引导模块", "执行步骤 — 项目概览");
            result.project_overview = await buildProjectOverview(config, projectPath);
            break;
        case "code_tour":
            debug("引导模块", "执行步骤 — 代码游览");
            result.code_tour = await buildCodeTour(config, projectPath, gitOps);
            break;
        case "knowledge_review":
            debug("引导模块", "执行步骤 — 知识库复习");
            result.knowledge_summary = buildKnowledgeSummary(knowledgeIndex);
            break;
        case "first_task":
            debug("引导模块", "执行步骤 — 首次任务建议");
            result.first_task_suggestion = await buildFirstTaskSuggestion(projectPath, gitOps);
            break;
    }
    // 将当前步骤标记为已完成
    if (!completedSteps.includes(currentStep)) {
        completedSteps.push(currentStep);
    }
    result.completed_steps = [...completedSteps];
    // 计算剩余步骤并生成下一步提示
    const remaining = ALL_STEPS.filter((s) => !completedSteps.includes(s));
    if (remaining.length > 0) {
        result.next_action_hint = `下一步：${STEP_DESCRIPTIONS[remaining[0]]}（${remaining[0]}）`;
    }
    else {
        result.next_action_hint = "引导已完成！您可以开始使用 SoloForge。";
    }
    // 尽力持久化会话进度（IO 失败不阻断流程）
    await persistSession(projectPath, currentStep, completedSteps).catch((e) => {
        debug("引导模块", "持久化会话进度失败", e);
    });
    return result;
}
// ── MCP 工具公共入口 (sf_onboard) ──
/**
 * MCP 工具层（sf_onboard）的公共入口。
 * @description 处理会话恢复逻辑：若非重置模式，先尝试加载已持久化的引导进度；
 *              若为重置模式，删除会话文件后从头开始。
 * @param input - MCP 工具输入参数（包含 reset 标志）
 * @returns 引导执行结果
 */
export async function onboard(input) {
    debug("引导模块", "MCP 公共入口调用，reset=" + input.reset);
    const { config, projectPath, gitOps, knowledgeIndex, currentStep, reset } = input;
    let completedSteps = [];
    if (reset) {
        debug("引导模块", "重置模式，清除会话文件");
        await deleteSessionFile(projectPath).catch((e) => { debug("引导模块", "删除会话文件失败", e); });
    }
    else {
        const session = await loadOnboardingSession(projectPath);
        if (session) {
            debug("引导模块", "加载已有会话，已完成步骤=" + session.completed_steps.length);
            completedSteps = session.completed_steps;
        }
        else {
            debug("引导模块", "无已有会话，从头开始");
        }
    }
    return runOnboarding({ config, projectPath, gitOps, knowledgeIndex, currentStep, completedSteps });
}
// ── 步骤: project_overview ──
/**
 * 构建项目概览（引导第一步：project_overview）。
 * @description 汇总项目名称、技术栈、仓库信息和编码惯例，为新人提供项目全貌。
 * @param config - 项目配置对象
 * @param projectPath - 项目根目录路径（用于解析惯例文件的相对路径）
 * @returns 项目概览数据，包含名称、技术栈、仓库列表和惯例
 */
async function buildProjectOverview(config, projectPath) {
    const { backend, frontend } = config.tech_stack;
    const techStack = `backend: ${backend.framework} ${backend.version}, frontend: ${frontend.framework} ${frontend.version}`;
    const repos = config.repos.map((r) => ({
        name: r.name,
        lang: r.lang,
        framework: r.framework,
    }));
    let conventions = [];
    if (config.conventions_file) {
        try {
            const conventionsPath = path.isAbsolute(config.conventions_file)
                ? config.conventions_file
                : path.join(projectPath, config.conventions_file);
            const content = await fs.readFile(conventionsPath, "utf-8");
            conventions = content
                .split("\n")
                .map((line) => line.trim())
                .filter((line) => line.length > 0 && !line.startsWith("#"));
        }
        catch {
            /* conventions file not found or unreadable */
        }
    }
    return {
        name: config.name,
        tech_stack: techStack,
        repos,
        conventions,
    };
}
// ── 步骤: code_tour ──
/**
 * 构建代码导览（引导第二步：code_tour）。
 * @description 扫描配置的作用域目录，识别关键模块和入口文件；结合 Git 提交记录发现近期活跃区域。
 * @param config - 项目配置对象
 * @param projectPath - 项目根目录路径
 * @param gitOps - Git 操作接口（用于获取提交历史）
 * @returns 代码导览数据，包含关键模块列表和入口文件列表
 */
async function buildCodeTour(config, projectPath, gitOps) {
    const keyModules = [];
    const allScopePaths = [...config.scope.backend, ...config.scope.frontend];
    // 遍历作用域路径以查找关键目录
    for (const scopeGlob of allScopePaths) {
        // 从 glob 模式中提取目录前缀（如 "src/**"、"web/pages/*"）
        const dirPrefix = scopeGlob.replace(/\/\*\*?$/, "").replace(/\/\*$/, "");
        const absDir = path.resolve(projectPath, dirPrefix);
        try {
            const entries = await fs.readdir(absDir, { withFileTypes: true });
            for (const entry of entries) {
                if (entry.isDirectory() && KEY_DIRECTORY_PATTERNS.includes(entry.name.toLowerCase())) {
                    keyModules.push({
                        path: path.join(dirPrefix, entry.name),
                        description: `${entry.name} 模块`,
                    });
                }
            }
        }
        catch {
            /* directory not found or unreadable */
        }
    }
    // 使用 git log 查找最近活跃区域
    try {
        const log = await gitOps.getLog(projectPath, { maxCount: 5 });
        const touchedFiles = new Set();
        for (const entry of log) {
            // 从提交消息中提取文件路径引用
            const matches = entry.message.matchAll(/[\w/.-]+\.\w{1,5}/g);
            for (const m of matches) {
                touchedFiles.add(m[0]);
            }
        }
        // 从触及文件中提取顶级目录（排除已收录的模块）
        for (const file of touchedFiles) {
            const parts = file.split("/");
            if (parts.length >= 2) {
                const dir = parts.slice(0, 2).join("/");
                if (!keyModules.some((km) => isPathInScope(km.path, dir, { caseSensitive: true }))) {
                    // 仅添加符合关键目录模式的目录（过滤掉无意义的文件引用）
                    const dirName = parts[1];
                    if (KEY_DIRECTORY_PATTERNS.some((p) => dirName.toLowerCase().includes(p))) {
                        keyModules.push({
                            path: dir,
                            description: `近期活跃区域（来自提交记录）`,
                        });
                    }
                }
            }
        }
    }
    catch {
        /* git log may fail */
    }
    // 在作用域根目录中查找入口文件
    const entryPoints = [];
    for (const scopeGlob of allScopePaths) {
        const dirPrefix = scopeGlob.replace(/\/\*\*?$/, "").replace(/\/\*$/, "");
        const absDir = path.resolve(projectPath, dirPrefix);
        try {
            const entries = await fs.readdir(absDir, { withFileTypes: true });
            for (const entry of entries) {
                if (entry.isFile() && ENTRY_POINT_PATTERNS.some((pattern) => pattern.test(entry.name))) {
                    const entryPath = path.join(dirPrefix, entry.name);
                    if (!entryPoints.includes(entryPath)) {
                        entryPoints.push(entryPath);
                    }
                }
            }
        }
        catch {
            /* directory not found */
        }
    }
    return {
        key_modules: keyModules,
        entry_points: entryPoints,
    };
}
// ── 步骤: knowledge_review ──
/**
 * 构建知识库摘要（引导第三步：knowledge_review）。
 * @description 从知识索引中统计活跃条目的总数、模式数、流程数，并提取最多 10 个关键主题。
 *              若知识索引不可用，返回空摘要。
 * @param knowledgeIndex - 知识索引管理器（可选）
 * @returns 知识库摘要，包含总数、分类计数和关键主题列表
 */
function buildKnowledgeSummary(knowledgeIndex) {
    if (!knowledgeIndex) {
        return {
            total: 0,
            patterns: 0,
            procedures: 0,
            key_topics: [],
        };
    }
    try {
        const allEntries = knowledgeIndex.getAllEntries();
        const combined = [...allEntries.global, ...allEntries.project];
        let patterns = 0;
        let procedures = 0;
        const keyTopics = new Set();
        for (const entry of combined) {
            if (entry.status !== "active" && entry.status !== "staged")
                continue;
            switch (entry.type) {
                case "pattern":
                    patterns++;
                    break;
                case "procedure":
                    procedures++;
                    break;
            }
            keyTopics.add(entry.name);
        }
        return {
            total: combined.filter((e) => e.status === "active" || e.status === "staged").length,
            patterns,
            procedures,
            key_topics: [...keyTopics].slice(0, 10),
        };
    }
    catch {
        return {
            total: 0,
            patterns: 0,
            procedures: 0,
            key_topics: [],
        };
    }
}
// ── 步骤: first_task ──
/**
 * 生成首个任务建议（引导第四步：first_task）。
 * @description 基于工作区状态和近期提交历史，智能推荐适合新人执行的第一个任务。
 *              检查维度包括：未提交变更、测试覆盖率、文档完善度、代码风格修复等。
 * @param projectPath - 项目根目录路径
 * @param gitOps - Git 操作接口（用于获取工作区状态和提交历史）
 * @returns 首个任务建议，包含意图描述、推荐理由和难度评级
 */
async function buildFirstTaskSuggestion(projectPath, gitOps) {
    const suggestions = [];
    // 检查未提交变更
    try {
        const status = await gitOps.getStatus(projectPath);
        if (status.length > 0) {
            suggestions.push({
                intent: "提交当前工作区变更",
                reason: `工作区有 ${status.length} 个未提交的变更`,
                difficulty: "low",
            });
        }
    }
    catch {
        /* status may fail */
    }
    // 分析最近提交历史以获取任务建议
    try {
        const log = await gitOps.getLog(projectPath, { maxCount: 10 });
        const recentMessages = log.map((e) => e.message.toLowerCase());
        // 检查是否有测试相关提交
        const hasTestMentions = recentMessages.some((m) => m.includes("test"));
        if (!hasTestMentions) {
            suggestions.push({
                intent: "补充测试覆盖率",
                reason: "近期提交中未发现测试相关变更，建议补充测试",
                difficulty: "low",
            });
        }
        // 检查是否有文档相关提交
        const hasDocMentions = recentMessages.some((m) => m.includes("doc") || m.includes("readme"));
        if (!hasDocMentions) {
            suggestions.push({
                intent: "补充项目文档",
                reason: "近期提交中未发现文档相关变更，建议补充文档",
                difficulty: "low",
            });
        }
        // 检查是否有 lint/格式修复提交
        const hasLintFixes = recentMessages.some((m) => m.includes("lint") || m.includes("style") || m.includes("format"));
        if (!hasLintFixes) {
            suggestions.push({
                intent: "修复 lint 警告",
                reason: "近期提交中未发现代码风格修复，建议运行 lint 检查",
                difficulty: "low",
            });
        }
        // 如果有最近提交，建议阅读它们
        if (log.length > 0) {
            suggestions.push({
                intent: "阅读近期提交，熟悉代码风格",
                reason: `最近有 ${log.length} 次提交，阅读它们有助于了解项目开发节奏`,
                difficulty: "low",
            });
        }
    }
    catch {
        /* git log may fail */
    }
    // 无其他建议时的默认选项
    if (suggestions.length === 0) {
        suggestions.push({
            intent: "阅读项目配置和入口文件",
            reason: "从了解项目基本结构开始",
            difficulty: "low",
        });
    }
    // 返回第一个建议
    return suggestions[0];
}
/**
 * 删除引导会话文件。
 * @description 用于重置引导进度时清理持久化文件。
 * @param projectPath - 项目根目录路径
 */
async function deleteSessionFile(projectPath) {
    const sessionFile = path.join(projectPath, ".soloforge", "state", "onboarding-session.json");
    await fs.unlink(sessionFile);
}
/**
 * 持久化引导会话进度。
 * @description 将当前步骤和已完成步骤列表写入 .soloforge/state/onboarding-session.json，
 *              实现断点续传。自动创建目录（如果不存在）。
 * @param projectPath - 项目根目录路径
 * @param currentStep - 当前完成的步骤
 * @param completedSteps - 已完成步骤列表
 */
async function persistSession(projectPath, currentStep, completedSteps) {
    const stateDir = path.join(projectPath, ".soloforge", "state");
    const sessionFile = path.join(stateDir, "onboarding-session.json");
    // 确保目录存在
    await fs.mkdir(stateDir, { recursive: true });
    const session = {
        current_step: currentStep,
        completed_steps: completedSteps,
        updated_at: new Date().toISOString(),
    };
    await fs.writeFile(sessionFile, JSON.stringify(session, null, 2), "utf-8");
}
/**
 * 加载已持久化的引导会话进度。
 * @description 从 .soloforge/state/onboarding-session.json 读取上次完成的步骤列表，
 *              用于断点续传。文件不存在或解析失败时返回 null。
 * @param projectPath - 项目根目录路径
 * @returns 会话数据（当前步骤和已完成步骤），若不存在则返回 null
 */
export async function loadOnboardingSession(projectPath) {
    debug("引导模块", "加载持久化会话");
    const sessionFile = path.join(projectPath, ".soloforge", "state", "onboarding-session.json");
    try {
        const content = await fs.readFile(sessionFile, "utf-8");
        const session = safeJsonParse(content);
        if (!session)
            return null;
        return {
            current_step: session.current_step,
            completed_steps: session.completed_steps,
        };
    }
    catch {
        debug("引导模块", "会话文件不存在或解析失败");
        return null;
    }
}
//# sourceMappingURL=onboarding.js.map