/**
 * notes.ts — SearchNotes / ReadNote / WriteNote tools
 *
 * Agents can search, read, and write shared knowledge notes
 * stored under data/users/{userId}/notes/{folder}/{file}.md.
 */

import { ToolDef } from '../../llm/openai-compat.js';
import { NotesService } from '../../notes/notes-service.js';
import type { ToolContext, ToolResult } from './core.js';

export interface NotesToolContext {
  notesService: NotesService;
  user: Express.User;
}

export const TOOL_DEFS: Record<string, ToolDef> = {
  SearchNotes: {
    type: 'function',
    function: {
      name: 'SearchNotes',
      description:
        '購読中の knowledge notes を全文検索 (FTS5)。' +
        '詳細は ReadToolDoc({ name: "SearchNotes" }) で取得可能。',
      parameters: {
        type: 'object',
        properties: {
          query: {
            type: 'string',
            description: '検索クエリ文字列（FTS5 構文）',
          },
          folder: {
            type: 'string',
            description: '特定フォルダーのみに絞り込む（省略可）',
          },
          limit: {
            type: 'integer',
            description: '最大取得件数（デフォルト 10、最大 100）',
          },
        },
        required: ['query'],
      },
    },
  },

  ReadNote: {
    type: 'function',
    function: {
      name: 'ReadNote',
      description:
        '特定 note の全文（frontmatter + 本文）を取得。可視性チェックあり。' +
        '詳細は ReadToolDoc({ name: "ReadNote" }) で取得可能。',
      parameters: {
        type: 'object',
        properties: {
          owner_id: {
            type: 'string',
            description: 'note の所有者 user ID',
          },
          folder: {
            type: 'string',
            description: 'フォルダー名',
          },
          file_name: {
            type: 'string',
            description: 'ファイル名（例: foo.md）',
          },
        },
        required: ['owner_id', 'folder', 'file_name'],
      },
    },
  },

  WriteNote: {
    type: 'function',
    function: {
      name: 'WriteNote',
      description:
        '自分の notes/{folder}/{file}.md に Markdown note を書き込む（作成 / 更新）。' +
        '詳細は ReadToolDoc({ name: "WriteNote" }) で取得可能。',
      parameters: {
        type: 'object',
        properties: {
          folder: {
            type: 'string',
            description: 'フォルダー名（英数字・. - _ のみ）',
          },
          file_name: {
            type: 'string',
            description: 'ファイル名（.md で終わる）',
          },
          content: {
            type: 'string',
            description: 'YAML frontmatter を含む完全な Markdown 内容',
          },
        },
        required: ['folder', 'file_name', 'content'],
      },
    },
  },
};

/**
 * Execute a notes tool.
 * Returns null when the tool name is not handled (allows index.ts to fall through).
 */
export async function executeNotesTools(
  name: string,
  args: Record<string, unknown>,
  ctx: NotesToolContext | ToolContext,
): Promise<ToolResult | null> {
  // Extract notes-specific context. When called via index.ts the ctx is a full
  // ToolContext; when called from tests it's a minimal NotesToolContext.
  const notesCtx = ctx as NotesToolContext & ToolContext;
  const notesService = notesCtx.notesService;

  if (!notesService) return null;

  // Reconstruct a minimal Express.User from ToolContext fields if needed.
  const user: Express.User = notesCtx.user ?? {
    id: notesCtx.userId ?? '',
    role: (notesCtx as ToolContext).notesUserRole ?? 'user',
    orgIds: (notesCtx as ToolContext).notesUserOrgIds ?? [],
    email: '',
    name: null,
    avatarUrl: null,
    status: 'active',
    defaultVisibility: 'private',
    defaultVisibilityOrgId: null,
  };

  switch (name) {
    case 'SearchNotes': {
      const rawQuery = String(args['query'] ?? '');
      // Wrap in FTS5 phrase quotes to avoid query syntax injection
      const safeQuery = '"' + rawQuery.replace(/"/g, '""') + '"';
      const rows = notesService.searchNotes({
        user,
        query: safeQuery,
        folder: args['folder'] ? String(args['folder']) : undefined,
        limit: args['limit'] ? Math.min(parseInt(String(args['limit']), 10), 100) : 10,
        consumerSubscribed: true,
      });
      if (rows.length === 0) {
        return { output: 'No notes found matching the query.', isError: false };
      }
      const lines = rows.map(
        (r) =>
          `- ${r.owner_id}/${r.folder}/${r.file_name}` +
          (r.title ? `: ${r.title}` : '') +
          (r.tags_json && r.tags_json !== '[]' ? ` [tags: ${r.tags_json}]` : '')
      );
      return { output: lines.join('\n'), isError: false };
    }

    case 'ReadNote': {
      const ownerId = String(args['owner_id'] ?? '');
      const folder = String(args['folder'] ?? '');
      const fileName = String(args['file_name'] ?? '');
      const out = notesService.getCrossUserNote({ user, ownerId, folder, fileName });
      if (!out) {
        return { output: 'Note not found or not accessible.', isError: true };
      }
      return { output: out.content, isError: false };
    }

    case 'WriteNote': {
      try {
        const result = notesService.writeNote({
          ownerId: user.id,
          folder: String(args['folder'] ?? ''),
          fileName: String(args['file_name'] ?? ''),
          content: String(args['content'] ?? ''),
        });
        return {
          output: `Wrote ${result.row.owner_id}/${result.row.folder}/${result.row.file_name}`,
          isError: false,
        };
      } catch (err) {
        return { output: `WriteNote error: ${(err as Error).message}`, isError: true };
      }
    }

    default:
      return null;
  }
}

// Re-export as executeTool for ToolModule interface compatibility
export const executeTool = executeNotesTools;
