// docs.ts — ツール詳細ドキュメント参照用ツール
//
// リポジトリ内 docs/tools/{name}.md を読み込んで返す。
// ワークスペース外の固定パスから読むため、Read ツールでは到達できない。
// Tool description にこのツールへのポインタを書いておくと、
// 詳細な使い方を必要に応じてエージェントが取得できる。

import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { ToolDef } from '../../llm/openai-compat.js';
import type { ToolContext, ToolResult } from './core.js';
import { logger } from '../../logger.js';

type McpToolLookup = (serverId: string, toolName: string) => { description: string | null; input_schema: string | null } | null;
let _mcpLookup: McpToolLookup | null = null;
export function setMcpToolLookup(fn: McpToolLookup | null): void {
  _mcpLookup = fn;
}

// dist/engine/tools/docs.js または src/engine/tools/docs.ts から
// リポジトリルートを解決し、docs/tools/ を指す
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
const DOCS_DIR = path.join(REPO_ROOT, 'docs', 'tools');

// 関連ツールが同じ doc を参照できるようエイリアスを定義
// キー・値ともに小文字
const TOOL_DOC_ALIASES: Record<string, string> = {
  // checklist.md にまとめる
  createchecklist: 'checklist',
  checkitem: 'checklist',
  getchecklist: 'checklist',
  // searchknowledge.md にまとめる
  listnamespaces: 'searchknowledge',
  listdocuments: 'searchknowledge',
  ingestdocument: 'searchknowledge',
  ingeststatus: 'searchknowledge',
  // x.ts ツールをまとめる
  xuserposts: 'xsearch',
  xpostdetail: 'xsearch',
  xfetchcardmedia: 'xsearch',
  xtimeline: 'xsearch',
  // youtube.ts をまとめる
  searchyoutube: 'getyoutubetranscript',
  // maps.ts をまとめる
  getdirections: 'searchplaces',
  reversegeocode: 'searchplaces',
  // office.ts をまとめる
  readpdf: 'office',
  readexcel: 'office',
  readdocx: 'office',
  readpptx: 'office',
  pdftoimages: 'office',
  splitexcelsheets: 'office',
  splitdocxsections: 'office',
  // pieces.ts をまとめる
  getpiece: 'listpieces',
  createpiece: 'listpieces',
  updatepiece: 'listpieces',
  // ms-learn.ts をまとめる
  fetchmicrosoftlearn: 'searchmicrosoftlearn',
  searchmicrosoftlearncache: 'searchmicrosoftlearn',
  refreshmicrosoftlearncache: 'searchmicrosoftlearn',
  // browser.ts: InteractiveBrowse / BrowseWithSession は browseweb.md にまとめる
  interactivebrowse: 'browseweb',
  browsewithsession: 'browseweb',
  // ssh.ts をまとめる
  sshexec: 'ssh-tools',
  sshupload: 'ssh-tools',
  sshdownload: 'ssh-tools',
  sshlistconnections: 'ssh-tools',
  // ssh-console.ts をまとめる
  sshconsoleensure: 'ssh-console-tools',
  sshconsolesend: 'ssh-console-tools',
  sshconsolesnapshot: 'ssh-console-tools',
  // slide.ts をまとめる
  settheme: 'slide',
  addslide: 'slide',
  buildpptx: 'slide',
  resetslides: 'slide',
  // notes.ts をまとめる
  searchnotes: 'notes',
  readnote: 'notes',
  writenote: 'notes',
};

const READ_TOOL_DOC_DEF: ToolDef = {
  type: 'function',
  function: {
    name: 'ReadToolDoc',
    description:
      'ツールの詳細な使い方ドキュメントを読み込む。各ツールの description は概要のみで、詳細な手順や例が必要なときはこれを呼ぶ。'
      + 'docs/tools/{name}.md（リポジトリ内固定パス）を参照する。',
    parameters: {
      type: 'object',
      properties: {
        name: {
          type: 'string',
          description: '読みたいツール名（例: "BrowseWeb", "SearchKnowledge"）',
        },
      },
      required: ['name'],
    },
  },
};

export const TOOL_DEFS: Record<string, ToolDef> = {
  ReadToolDoc: READ_TOOL_DOC_DEF,
};

export async function executeTool(
  name: string,
  input: Record<string, unknown>,
  _ctx: ToolContext,
): Promise<ToolResult | null> {
  if (name !== 'ReadToolDoc') return null;

  const toolName = input['name'] as string | undefined;
  if (!toolName || typeof toolName !== 'string') {
    return { output: 'ReadToolDoc error: name パラメータが必要です', isError: true };
  }

  // MCP ツール名 (mcp__<serverId>__<toolName>) の場合はキャッシュから返す
  if (toolName.startsWith('mcp__')) {
    // Lazy-import to avoid a hard dependency on mcp module at module-load time.
    const { parseToolName } = await import('../../mcp/tool-adapter.js');
    const parsed = parseToolName(toolName);
    if (!parsed) {
      return { output: `ReadToolDoc: 不正な MCP ツール名 "${toolName}"`, isError: true };
    }
    if (!_mcpLookup) {
      return { output: 'ReadToolDoc: MCP サブシステムが初期化されていません', isError: true };
    }
    const row = _mcpLookup(parsed.serverId, parsed.toolName);
    if (!row) {
      return { output: `ReadToolDoc: ${toolName} のキャッシュ情報がありません`, isError: false };
    }
    let schemaBlock = '';
    if (row.input_schema) {
      try {
        const schema = JSON.parse(row.input_schema);
        schemaBlock = `\n\n## Input schema\n\n\`\`\`json\n${JSON.stringify(schema, null, 2)}\n\`\`\``;
      } catch {
        schemaBlock = `\n\n## Input schema (raw)\n\n\`\`\`\n${row.input_schema}\n\`\`\``;
      }
    }
    return {
      output: `# ${toolName}\n\n${row.description ?? '(no description)'}${schemaBlock}`,
      isError: false,
    };
  }

  // パストラバーサル防止: 英数字とハイフン・アンダースコアのみ許可
  if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(toolName)) {
    return { output: `ReadToolDoc error: 不正なツール名 "${toolName}"`, isError: true };
  }

  const lowerName = toolName.toLowerCase();
  const resolvedName = TOOL_DOC_ALIASES[lowerName] ?? lowerName;
  const docPath = path.join(DOCS_DIR, `${resolvedName}.md`);
  try {
    const content = await fs.promises.readFile(docPath, 'utf-8');
    return { output: content, isError: false };
  } catch (e: any) {
    if (e.code === 'ENOENT') {
      // 利用可能なドキュメント一覧を返す
      try {
        const files = await fs.promises.readdir(DOCS_DIR);
        const available = files
          .filter((f) => f.endsWith('.md'))
          .map((f) => f.replace(/\.md$/, ''))
          .sort();
        return {
          output: `ReadToolDoc: "${toolName}" のドキュメントは存在しません。\n利用可能なドキュメント:\n${available.map((n) => `- ${n}`).join('\n')}`,
          isError: true,
        };
      } catch {
        return { output: `ReadToolDoc: "${toolName}" のドキュメントは存在しません。`, isError: true };
      }
    }
    logger.warn(`[ReadToolDoc] failed to read ${docPath}: ${e.message}`);
    return { output: `ReadToolDoc error: ${e.message}`, isError: true };
  }
}
