/**
 * Project Caffeine v0.1.1
 * Copyright (c) 2025-2026 Gitconomy Research
 *
 * SPDX-License-Identifier: MIT
 * 
 * Contributors:
 * - 郭晧 <guohao@gitconomy.org> (Initial Author)
 */

import fs from 'fs/promises';
import path from 'path';

/**
 * 本地知识库的根目录路径。
 * 
 * 该目录存放所有 Markdown 笔记文件，所有文件操作均限定在此目录内，
 * 以防止路径遍历攻击。
 * 
 * @constant {string}
 */


const OBSIDIAN_VAULT_PATH = '/home/wguo/Downloads/MyVault'; // 【⚠️ 重要配置】请修改为你电脑上真实的 Markdown 笔记文件夹绝对路径！

/**
 * 列出知识库中所有 Markdown 笔记的文件名。
 * 
 * 该函数读取 OBSIDIAN_VAULT_PATH 目录下的所有文件，过滤出以 .md 结尾
 * （不区分大小写）的文件，并返回文件名列表。若目录不存在或无权限访问，
 * 则返回空数组并打印错误日志。
 * 
 * @returns {Promise<string[]>} 包含所有笔记文件名的数组，若失败则返回空数组。
 */

export async function listObsidianNotes(): Promise<string[]> {
    try {
        const files = await fs.readdir(OBSIDIAN_VAULT_PATH);
        return files.filter(file => file.toLowerCase().endsWith('.md'));
    } catch (error: any) {
        console.error(`[Project Caffeine] 无法读取知识库目录: ${error.message}`);
        return [];
    }
}

/**
 * 读取指定笔记文件的完整内容。
 * 
 * 该函数首先对文件名进行安全校验，确保文件位于知识库目录内，
 * 防止路径遍历攻击。校验通过后，读取文件内容并返回。
 * 
 * @param {string} filename - 要读取的笔记文件名（必须包含 .md 后缀）
 * @returns {Promise<string>} 笔记文件的文本内容
 * @throws {Error} 当文件名导致路径越界时抛出安全警告
 * @throws {Error} 当文件不存在或无权限读取时抛出错误
 */

export async function readObsidianNote(filename: string): Promise<string> {
    const targetPath = path.resolve(OBSIDIAN_VAULT_PATH, filename);
    const safeVaultPath = path.resolve(OBSIDIAN_VAULT_PATH);

    // 核心防御：防止大模型通过传入 "../../" 读取系统敏感文件
    if (!targetPath.startsWith(safeVaultPath)) {
        throw new Error(`安全警告：越权访问拦截！禁止读取目录外的文件: ${filename}`);
    }

    try {
        const content = await fs.readFile(targetPath, 'utf-8');
        return content;
    } catch (error: any) {
        throw new Error(`无法读取笔记 [${filename}]: 文件可能不存在或无权限。`);
    }
}

/**
 * 保存笔记到本地知识库。
 * 
 * 该函数将内容写入指定文件，执行以下校验和操作：
 * 1. 验证文件名是否以 .md 结尾。
 * 2. 验证文件路径是否在知识库目录内，防止路径遍历攻击。
 * 3. 确保知识库目录存在（若不存在则自动创建）。
 * 4. 将内容写入文件。
 * 
 * @param {string} filename - 笔记文件名（必须以 .md 结尾）
 * @param {string} content - 笔记内容（Markdown 格式）
 * @returns {Promise<string>} 保存成功的提示信息，包含文件绝对路径
 * @throws {Error} 当文件名不以 .md 结尾时抛出错误
 * @throws {Error} 当文件名导致路径越界时抛出错误
 * @throws {Error} 当目录创建失败或文件写入失败时抛出错误
 */

export async function saveNote(filename: string, content: string): Promise<string> {
  // 1. 验证文件名是否以 .md 结尾
  if (!filename.endsWith('.md')) {
    throw new Error('文件名必须以 .md 结尾');
  }

  // 2. 防止路径遍历攻击：解析绝对路径，并检查是否在 NOTES_DIR 下
  const fullPath = path.resolve(OBSIDIAN_VAULT_PATH, filename);
  const relative = path.relative(OBSIDIAN_VAULT_PATH, fullPath);
  if (relative.startsWith('..') || path.isAbsolute(relative)) {
    throw new Error('无效的文件名，不允许访问上层目录');
  }

  // 3. 确保目标目录存在（可选，如果 NOTES_DIR 必须存在则可跳过）
  await fs.mkdir(OBSIDIAN_VAULT_PATH, { recursive: true });

  // 4. 写入文件
  await fs.writeFile(fullPath, content, 'utf-8');
  return `笔记已保存至: ${fullPath}`;
}