/**
 * Shared folder creation for network callers (web route + MCP tool).
 *
 * Wraps Workspace.registerRepo() with the concerns that are common to every
 * network-facing creation path: kebab-case name validation, reserved-prefix
 * guards, duplicate detection, folder-admin membership grant for the creator,
 * and a folder_created WS broadcast.
 *
 * The CLI (`studiograph folder add`) deliberately does NOT use this helper —
 * it runs locally with filesystem privileges, has no user identity to grant
 * membership to, and no WS hub to broadcast on. It calls registerRepo()
 * directly (see src/cli/commands/folder.ts).
 *
 * The private-folder auto-provision path (`ensurePrivateFolder` in
 * src/server/routes/auth-api.ts) also calls registerRepo() directly because
 * it has different semantics: private=true, no folder-admin grant, one per
 * user. Not a fit for this helper.
 */
import { Workspace } from '../core/workspace.js';
export class FolderCreationError extends Error {
    code;
    constructor(code, message) {
        super(message);
        this.code = code;
        this.name = 'FolderCreationError';
    }
}
const SLUG_REGEX = /^[a-z0-9][a-z0-9-]*$/;
const MAX_NAME_LENGTH = 64;
/**
 * Reserved for per-user private folder auto-provisioning
 * (`entries-<userId>` in auth-api.ts#ensurePrivateFolder). Letting a
 * shared folder squat this prefix would block a new user's private
 * provisioning on login.
 */
const RESERVED_NAME_REGEX = /^entries-\d+$/;
export async function createSharedFolder(workspaceManager, authService, input, user, wsHub) {
    const name = typeof input.name === 'string' ? input.name : '';
    const display_name = input.display_name;
    const description = input.description;
    if (!name || !SLUG_REGEX.test(name)) {
        throw new FolderCreationError('invalid_name', 'Folder name must be kebab-case (lowercase letters, digits, hyphens; must start with a letter or digit)');
    }
    if (name.length > MAX_NAME_LENGTH) {
        throw new FolderCreationError('invalid_name', `Folder name must be ${MAX_NAME_LENGTH} characters or fewer`);
    }
    if (RESERVED_NAME_REGEX.test(name)) {
        throw new FolderCreationError('invalid_name', 'Folder name "entries-<number>" is reserved for per-user private folders');
    }
    const workspace = new Workspace(workspaceManager.workspacePath);
    let repo;
    try {
        repo = await workspace.registerRepo({
            name,
            display_name: display_name || undefined,
            description,
            owner_id: user?.id,
        });
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        if (/already registered/i.test(msg)) {
            throw new FolderCreationError('duplicate', `Folder "${name}" already exists`);
        }
        throw new FolderCreationError('internal', msg);
    }
    workspaceManager.reloadConfig();
    if (user)
        authService.grantFolderAccess(user.id, name, 'admin');
    // For an authenticated creator, deliver to that user's own sessions only.
    // A regular `broadcast()` would be filtered by `canReceive()` against each
    // client's connection-time folder snapshot — which by definition doesn't
    // contain a brand-new folder — so the event would be silently dropped.
    // Patch the snapshot via `addUserFolder` so subsequent `entity_change`
    // broadcasts for entities in this folder reach the creator without
    // requiring a reconnect.
    //
    // For anonymous / API-without-identity creates (CLI, scripts), fall back
    // to a plain broadcast. Open-mode clients (`client.folders === null`)
    // bypass canReceive and will pick it up; auth-mode clients would have
    // dropped it anyway, which preserves the prior behavior.
    if (wsHub) {
        if (user) {
            wsHub.addUserFolder(String(user.id), name);
            wsHub.broadcastToUser(String(user.id), {
                type: 'folder_created',
                repo: name,
                actor: user.displayName,
                timestamp: new Date().toISOString(),
            });
        }
        else {
            wsHub.broadcast({
                type: 'folder_created',
                repo: name,
                actor: 'API',
                timestamp: new Date().toISOString(),
            });
        }
    }
    return repo;
}
//# sourceMappingURL=folder-creation.js.map