import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
import { join } from 'path';
/**
 * Per-folder sidecar carrying an optional `display_name` override and a stable
 * folder `id`. Lives inside the folder itself so it travels atomically with FS
 * rename / move / cross-repo transfer — the same way an entity .md file's
 * frontmatter travels with the file.
 *
 * The `id` is assigned eagerly in mutation paths (folder creation); reads never
 * generate one. Callers that don't find an id fall back to a path-based key.
 */
export const FOLDER_SIDECAR = '.studiograph-folder.json';
export function folderSidecarPath(absDir) {
    return join(absDir, FOLDER_SIDECAR);
}
/**
 * Read the full sidecar object for a folder, or null when the sidecar is
 * absent / unreadable / malformed. Only the recognized fields are surfaced.
 */
export function readFolderSidecarData(absDir) {
    const sidecarPath = folderSidecarPath(absDir);
    if (!existsSync(sidecarPath))
        return null;
    try {
        const parsed = JSON.parse(readFileSync(sidecarPath, 'utf-8'));
        const data = {};
        if (typeof parsed?.display_name === 'string' && parsed.display_name.trim()) {
            data.display_name = parsed.display_name.trim();
        }
        if (typeof parsed?.id === 'string' && parsed.id.trim()) {
            data.id = parsed.id.trim();
        }
        return data;
    }
    catch {
        return null;
    }
}
/** Read just the display_name override, or null. (Back-compat helper.) */
export function readFolderSidecar(absDir) {
    return readFolderSidecarData(absDir)?.display_name ?? null;
}
/** Read just the stable folder id, or null when unassigned. */
export function readFolderSidecarId(absDir) {
    return readFolderSidecarData(absDir)?.id ?? null;
}
/**
 * Merge-safe write: reads the existing sidecar, applies the patch, and writes
 * the result — so writing one field never clobbers the other. A field present
 * in `patch` but empty/undefined is removed; a field absent from `patch` is
 * left untouched. If the merge leaves the sidecar empty, the file is removed.
 * Returns the sidecar path (for git staging), or null when the file was
 * removed because nothing remained.
 */
export function writeFolderSidecarData(absDir, patch) {
    const merged = { ...(readFolderSidecarData(absDir) ?? {}) };
    if ('display_name' in patch) {
        const dn = patch.display_name?.trim();
        if (dn)
            merged.display_name = dn;
        else
            delete merged.display_name;
    }
    if ('id' in patch) {
        const id = patch.id?.trim();
        if (id)
            merged.id = id;
        else
            delete merged.id;
    }
    const sidecarPath = folderSidecarPath(absDir);
    if (Object.keys(merged).length === 0) {
        return removeFolderSidecar(absDir);
    }
    mkdirSync(absDir, { recursive: true });
    writeFileSync(sidecarPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
    return sidecarPath;
}
/** Write/merge a display_name override (preserves any existing id). */
export function writeFolderSidecar(absDir, displayName) {
    // Non-null: a non-empty displayName always leaves the sidecar non-empty.
    return writeFolderSidecarData(absDir, { display_name: displayName });
}
export function removeFolderSidecar(absDir) {
    const sidecarPath = folderSidecarPath(absDir);
    if (!existsSync(sidecarPath))
        return null;
    rmSync(sidecarPath, { force: true });
    return sidecarPath;
}
//# sourceMappingURL=folder-sidecar.js.map