/**
 * Folder Management Tools for the Studiograph Agent
 *
 * Five tools that let the agent operate on the folder layout within a repo:
 *   - list_folders     — read the folder tree of a repo
 *   - create_subfolder — create a new subfolder inside an existing repo
 *                        (distinct from graph-tools' `create_folder`, which
 *                        creates a brand-new top-level repo)
 *   - rename_folder    — rename / move within the same repo
 *   - move_entity      — relocate an entry to a different folder within the
 *                        same repo (distinct from graph-tools'
 *                        `transfer_entity`, which moves across repos)
 *   - delete_folder    — remove a folder (recursive opt-in, blast-radius reported)
 *
 * The system prompt restraint rule applies to all of these:
 * never reorganize/rename/delete without explicit user request.
 */
import { Type as T } from 'typebox';
import * as access from '../../services/access-control.js';
import { wrapToolResult } from './graph-tools.js';
function canAccessRepo(repoName, workspaceManager, getCurrentUser, authService) {
    const user = getCurrentUser();
    if (!user)
        return true;
    if (!authService)
        return true;
    return access.hasRepoAccess(user, repoName, workspaceManager, authService);
}
export function createFolderTools(workspaceManager, gitUser, onMutation, getCurrentUser, authService, 
/**
 * Same activity hook as createGraphTools. Currently accepted but not wrapped
 * around individual folder ops — folder mutations are rare relative to entity
 * mutations, and the chip wouldn't have time to render before completing.
 * Param exists so the orchestrator can pass a single callback symmetrically.
 */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_onAgentActivity) {
    const getUser = getCurrentUser ?? (() => null);
    const authSvc = authService ?? null;
    function effectiveGitUser() {
        const user = getUser();
        if (user?.handle) {
            return {
                id: user.handle,
                name: user.displayName ?? user.handle,
                email: `${user.handle}@studiograph.local`,
            };
        }
        return gitUser;
    }
    function resolveRepoOrFail(repoName) {
        if (!canAccessRepo(repoName, workspaceManager, getUser, authSvc)) {
            return { error: `Folder "${repoName}" not found` };
        }
        try {
            return { graph: workspaceManager.getGraph(repoName) };
        }
        catch (err) {
            return { error: err?.message ?? `Could not open folder "${repoName}"` };
        }
    }
    return [
        // ── list_folders ────────────────────────────────────────────────
        {
            name: 'list_folders',
            label: 'List Folders',
            description: 'List every nested folder inside a repo, returning repo-relative POSIX paths and any explicit display_name override. Empty array means the repo has no subfolders. Use this BEFORE creating a new folder to avoid duplicating an existing one.',
            parameters: T.Object({
                repo: T.String({ description: 'Folder name to inspect' }),
            }),
            execute: async (_toolCallId, params) => {
                try {
                    const resolved = resolveRepoOrFail(params.repo);
                    if ('error' in resolved)
                        return wrapToolResult({ success: false, error: resolved.error });
                    const folders = resolved.graph.listFolders().map((f) => {
                        const entry = { path: f.path };
                        if (f.display_name)
                            entry.display_name = f.display_name;
                        return entry;
                    });
                    return wrapToolResult({
                        success: true,
                        repo: params.repo,
                        folders,
                        message: folders.length === 0
                            ? `Folder "${params.repo}" has no subfolders.`
                            : `Found ${folders.length} subfolder${folders.length === 1 ? '' : 's'} in "${params.repo}".`,
                    });
                }
                catch (err) {
                    return wrapToolResult({ success: false, error: err instanceof Error ? err.message : 'Unknown error' });
                }
            },
        },
        // ── create_subfolder ─────────────────────────────────────────────
        // Distinct from graph-tools' `create_folder`, which creates a new
        // top-level shared repo. This one nests inside an existing repo.
        {
            name: 'create_subfolder',
            label: 'Create Subfolder',
            description: 'Create a new subfolder inside an existing repo. Path is repo-relative POSIX (e.g. "clients/acme"). Parent segments are auto-created. Pass display_name to override the title-cased fallback (use when the user wants casing the folder name can\'t express, e.g. "HR" or "Schema_OS"). For creating a brand-new top-level shared folder, use create_folder instead. Confirm with the user before calling — folders shape how the user organises their work.',
            parameters: T.Object({
                repo: T.String({ description: 'Folder name to create the subfolder inside' }),
                path: T.String({ description: 'Repo-relative POSIX path of the new subfolder (e.g. "clients/acme")' }),
                display_name: T.Optional(T.String({ description: 'Optional human-readable label. Omit when the title-cased folder name is the right display.' })),
            }),
            execute: async (_toolCallId, params) => {
                try {
                    const resolved = resolveRepoOrFail(params.repo);
                    if ('error' in resolved)
                        return wrapToolResult({ success: false, error: resolved.error });
                    const actor = effectiveGitUser();
                    const { alreadyExisted } = await resolved.graph.createFolder(params.path, undefined, actor, {
                        displayName: params.display_name,
                    });
                    // entityType "_folder" is the existing sentinel (see graph-tools'
                    // create_folder) that tells the client this is a folder-tree
                    // mutation, not an entry mutation — the realtime handler maps it
                    // to refreshRepoAfterFolderMutation so the sidebar + folder list
                    // view pick up the new subfolder without a manual reload.
                    onMutation?.({ action: 'created', repo: params.repo, entityType: '_folder', entityId: params.path });
                    // Be honest when the folder was already there — createFolder is an
                    // idempotent ensure, so a duplicate path "succeeds" but creates nothing.
                    // Telling the agent it's a reuse stops it from claiming a new folder
                    // (and silently merging entries into the existing one).
                    return wrapToolResult({
                        success: true,
                        alreadyExisted,
                        repo: params.repo,
                        path: params.path,
                        message: alreadyExisted
                            ? `Folder ${params.repo}/${params.path} already exists — reusing it (nothing created).`
                            : `Created folder ${params.repo}/${params.path}. Auto-committed.`,
                    });
                }
                catch (err) {
                    return wrapToolResult({ success: false, error: err instanceof Error ? err.message : 'Unknown error' });
                }
            },
        },
        // ── rename_folder ────────────────────────────────────────────────
        {
            name: 'rename_folder',
            label: 'Rename Folder',
            description: 'Rename or move a subfolder within the same repo, optionally updating its display_name in the same commit. To update only the display_name without moving, set `to` equal to `from`. ALL descendant entries follow when the path changes — their on-disk paths change. NEVER call this without explicit user instruction. Surface what will move ("Rename clients/acme → clients/acme-corp and update path of N entries?") and wait for confirmation.',
            parameters: T.Object({
                repo: T.String({ description: 'Folder name containing the subfolder' }),
                from: T.String({ description: 'Current repo-relative path of the subfolder' }),
                to: T.String({ description: 'New repo-relative path of the subfolder. Pass the same value as `from` to update display_name only.' }),
                display_name: T.Optional(T.String({ description: 'New human-readable label. Pass empty string to clear the override and revert to the title-cased folder name.' })),
            }),
            execute: async (_toolCallId, params) => {
                try {
                    const resolved = resolveRepoOrFail(params.repo);
                    if ('error' in resolved)
                        return wrapToolResult({ success: false, error: resolved.error });
                    const actor = effectiveGitUser();
                    await resolved.graph.renameFolder(params.from, params.to, undefined, actor, {
                        displayName: params.display_name === undefined ? undefined : params.display_name,
                    });
                    onMutation?.({ action: 'updated', repo: params.repo, entityType: '_folder', entityId: params.to });
                    return wrapToolResult({
                        success: true,
                        repo: params.repo,
                        from: params.from,
                        to: params.to,
                        message: `Renamed ${params.repo}/${params.from} → ${params.repo}/${params.to}. Auto-committed.`,
                    });
                }
                catch (err) {
                    return wrapToolResult({ success: false, error: err instanceof Error ? err.message : 'Unknown error' });
                }
            },
        },
        // ── move_entity ──────────────────────────────────────────────────
        {
            name: 'move_entity',
            label: 'Move Entry',
            description: 'Move an entry to a different folder within the SAME repo. Only the entry\'s folder_path changes — its id and type are preserved. For cross-repo moves the user should use the bulk-move dialog (no agent tool yet).',
            parameters: T.Object({
                repo: T.String({ description: 'Folder name containing the entry' }),
                entityType: T.String({ description: 'Entity type of the entry being moved' }),
                entityId: T.String({ description: 'ID of the entry being moved' }),
                folder_path: T.String({ description: 'Target repo-relative folder path. Empty string = repo root.' }),
            }),
            execute: async (_toolCallId, params) => {
                try {
                    const resolved = resolveRepoOrFail(params.repo);
                    if ('error' in resolved)
                        return wrapToolResult({ success: false, error: resolved.error });
                    const actor = effectiveGitUser();
                    const moved = await resolved.graph.move(params.entityType, params.entityId, params.folder_path, undefined, actor);
                    onMutation?.({ action: 'updated', repo: params.repo, entityType: params.entityType, entityId: params.entityId });
                    return wrapToolResult({
                        success: true,
                        repo: params.repo,
                        entityType: params.entityType,
                        entityId: params.entityId,
                        folder_path: params.folder_path,
                        path: moved.path,
                        message: `Moved ${params.entityType}/${params.entityId} to ${params.repo}/${params.folder_path || '(root)'}. Auto-committed.`,
                    });
                }
                catch (err) {
                    return wrapToolResult({ success: false, error: err instanceof Error ? err.message : 'Unknown error' });
                }
            },
        },
        // ── delete_folder ────────────────────────────────────────────────
        {
            name: 'delete_folder',
            label: 'Delete Folder',
            description: 'Delete a subfolder. Defaults to non-recursive — if the folder has entries or subfolders the call FAILS with a blast_radius payload (entriesDeleted, subfoldersDeleted) so you can present the impact to the user. Re-invoke with recursive: true ONLY after explicit user confirmation. NEVER pass recursive: true on the first attempt.',
            parameters: T.Object({
                repo: T.String({ description: 'Folder name containing the subfolder to delete' }),
                path: T.String({ description: 'Repo-relative path of the subfolder to delete' }),
                recursive: T.Optional(T.Boolean({ description: 'Set to true ONLY after the user has confirmed the blast radius. Defaults to false.' })),
            }),
            execute: async (_toolCallId, params) => {
                try {
                    const resolved = resolveRepoOrFail(params.repo);
                    if ('error' in resolved)
                        return wrapToolResult({ success: false, error: resolved.error });
                    const actor = effectiveGitUser();
                    const result = await resolved.graph.deleteFolder(params.path, {
                        recursive: params.recursive === true,
                        user: actor,
                    });
                    onMutation?.({ action: 'deleted', repo: params.repo, entityType: '_folder', entityId: params.path });
                    return wrapToolResult({
                        success: true,
                        repo: params.repo,
                        path: params.path,
                        entriesDeleted: result.entriesDeleted,
                        subfoldersDeleted: result.subfoldersDeleted,
                        message: `Deleted folder ${params.repo}/${params.path}${result.entriesDeleted > 0 || result.subfoldersDeleted > 0 ? ` (${result.entriesDeleted} entries, ${result.subfoldersDeleted} subfolders)` : ''}. Auto-committed.`,
                    });
                }
                catch (err) {
                    // Surface the blast radius so the agent can ask the user before retrying recursively.
                    if (err?.code === 'FOLDER_NOT_EMPTY' && err.blastRadius) {
                        return wrapToolResult({
                            success: false,
                            code: 'FOLDER_NOT_EMPTY',
                            blast_radius: err.blastRadius,
                            message: `Folder ${params.repo}/${params.path} is not empty: ${err.blastRadius.entriesDeleted} entries, ${err.blastRadius.subfoldersDeleted} subfolders. Confirm with the user before re-invoking with recursive: true.`,
                        });
                    }
                    return wrapToolResult({ success: false, error: err instanceof Error ? err.message : 'Unknown error' });
                }
            },
        },
    ];
}
//# sourceMappingURL=folder-tools.js.map