/**
 * Project Caffeine v0.0.3
 * 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';

/**
 * 本地知识库的根目录路径。
 * @constant {string}
 */
const OBSIDIAN_VAULT_PATH = '/home/wguo/Downloads/MyVault'; // 【⚠️ 重要配置】请修改为你电脑上真实的 Markdown 笔记文件夹绝对路径！

/**
 * 列出知识库中所有 Markdown 笔记的文件名。
 * 
 * @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 {
    return await fs.readFile(targetPath, 'utf-8');
  } catch (error: any) {
    throw new Error(`无法读取笔记 [${filename}]: 文件可能不存在或无权限。`);
  }
}

/**
 * 保存笔记到本地知识库。
 * 
 * @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> {
  if (!filename.endsWith('.md')) {
    throw new Error('文件名必须以 .md 结尾');
  }

  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('无效的文件名，不允许访问上层目录');
  }

  await fs.mkdir(OBSIDIAN_VAULT_PATH, { recursive: true });
  await fs.writeFile(fullPath, content, 'utf-8');
  return `笔记已保存至: ${fullPath}`;
}

/**
 * 在笔记内容中搜索关键词（忽略大小写）。
 * 
 * @param {string[]} keywords - 关键词数组
 * @returns {Promise<Array<{ filename: string; snippets: string[] }>>} 匹配的笔记列表，
 *          每个包含文件名和上下文片段（最多5条）
 */
export async function searchNotes(keywords: string[]): Promise<Array<{ filename: string; snippets: string[] }>> {
  const files = await listObsidianNotes();
  const results: Array<{ filename: string; snippets: string[] }> = [];

  for (const file of files) {
    try {
      const content = await readObsidianNote(file);
      const lowerContent = content.toLowerCase();

      if (keywords.every(kw => lowerContent.includes(kw.toLowerCase()))) {
        const snippets: string[] = [];
        for (const kw of keywords) {
          const index = lowerContent.indexOf(kw.toLowerCase());
          if (index !== -1) {
            const start = Math.max(0, index - 30);
            const end = Math.min(content.length, index + kw.length + 30);
            let snippet = content.substring(start, end);
            if (start > 0) snippet = '...' + snippet;
            if (end < content.length) snippet = snippet + '...';
            snippets.push(snippet);
          }
        }
        results.push({ filename: file, snippets });
      }
    } catch (e) {
      // 忽略读取失败的文件（如权限问题）
    }
  }

  return results.slice(0, 5);
}