import { internalQueryBuilder } from "../../engine/internal-query-builder.js";
import { normalizeDirectoryPath, normalizeFilePath, normalizePathSegment, } from "../path.js";
export function getActiveVersionId(engine) {
    const result = engine.executeSync(internalQueryBuilder
        .selectFrom("active_version")
        .select("version_id")
        .compile()).rows;
    const versionId = result[0]?.version_id;
    if (!versionId) {
        throw new Error("No active version present");
    }
    return versionId;
}
export function readDirectoryByPath(args) {
    if (args.path === "/") {
        return undefined;
    }
    const segments = args.path.slice(1, -1).split("/");
    let parentId = null;
    let lastRow;
    for (const segment of segments) {
        const result = args.engine.executeSync({
            sql: `
				SELECT
					json_extract(snapshot_content, '$.id') AS id,
					json_extract(snapshot_content, '$.parent_id') AS parent_id
				FROM state_by_version
				WHERE schema_key = 'lix_directory_descriptor'
					AND version_id = ?
					AND json_extract(snapshot_content, '$.name') = ?
					AND ${parentId === null ? "json_extract(snapshot_content, '$.parent_id') IS NULL" : "json_extract(snapshot_content, '$.parent_id') = ?"}
				LIMIT 1;
			`,
            parameters: parentId === null
                ? [args.versionId, segment]
                : [args.versionId, segment, parentId],
        }).rows;
        const row = result[0];
        if (!row) {
            return undefined;
        }
        lastRow = {
            id: row.id,
            parent_id: row.parent_id ?? null,
        };
        parentId = lastRow.id;
    }
    if (!lastRow) {
        return undefined;
    }
    return { id: lastRow.id, parent_id: lastRow.parent_id, path: args.path };
}
function readDirectoryDescriptorById(args) {
    const rows = args.engine.executeSync(internalQueryBuilder
        .selectFrom("state_by_version")
        .where("schema_key", "=", "lix_directory_descriptor")
        .where("version_id", "=", args.versionId)
        .where("entity_id", "=", args.directoryId)
        .select(["snapshot_content"])
        .compile()).rows;
    const raw = rows[0]?.snapshot_content;
    if (!raw) {
        return undefined;
    }
    const snapshot = typeof raw === "string" ? JSON.parse(raw) : raw;
    return snapshot;
}
export function readDirectoryPathById(args) {
    return composeDirectoryPath({
        engine: args.engine,
        versionId: args.versionId,
        directoryId: args.directoryId,
    });
}
export function assertNoFileAtPath(args) {
    const rows = args.engine.executeSync(internalQueryBuilder
        .selectFrom("file_by_version")
        .where("path", "=", args.filePath)
        .where("lixcol_version_id", "=", args.versionId)
        .select(["id"])
        .compile()).rows;
    if (rows.length > 0) {
        throw new Error(`Directory path collides with existing file path: ${args.filePath}`);
    }
}
export function assertNoDirectoryAtFilePath(args) {
    const directoryPath = `${args.filePath}/`;
    const rows = args.engine.executeSync(internalQueryBuilder
        .selectFrom("directory_by_version")
        .where("path", "=", directoryPath)
        .where("lixcol_version_id", "=", args.versionId)
        .select(["id"])
        .compile()).rows;
    if (rows.length > 0) {
        throw new Error(`File path collides with existing directory path: ${directoryPath}`);
    }
}
export function computeDirectoryPath(args) {
    const rawName = args.name?.trim();
    const name = normalizePathSegment(rawName ?? "");
    if (!name) {
        throw new Error("Directory name must be provided");
    }
    const parentPath = args.parentId
        ? composeDirectoryPath({
            engine: args.engine,
            versionId: args.versionId,
            directoryId: args.parentId,
        })
        : "/";
    if (!parentPath) {
        throw new Error(`Parent directory does not exist for id ${args.parentId}`);
    }
    const candidatePath = normalizeDirectoryPath(`${parentPath}${name}/`);
    assertNoFileAtPath({
        engine: args.engine,
        versionId: args.versionId,
        filePath: candidatePath.slice(0, -1),
    });
    return candidatePath;
}
function listAncestorDirectories(filePath) {
    const normalizedPath = normalizeFilePath(filePath);
    const segments = normalizedPath.split("/").filter(Boolean);
    segments.pop();
    const paths = [];
    let current = "";
    for (const segment of segments) {
        const normalizedSegment = normalizePathSegment(segment);
        current += `/${normalizedSegment}`;
        paths.push(`${current}/`.normalize("NFC"));
    }
    return paths;
}
export function ensureDirectoryAncestors(args) {
    const versionId = args.versionId ?? getActiveVersionId(args.engine);
    const directories = listAncestorDirectories(args.filePath);
    if (directories.length === 0)
        return;
    let parentId = null;
    for (const dirPath of directories) {
        const existing = readDirectoryByPath({
            engine: args.engine,
            versionId,
            path: dirPath,
        });
        if (existing) {
            parentId = existing.id;
            continue;
        }
        const name = normalizePathSegment(dirPath.slice(1, -1).split("/").pop() ?? "");
        const result = args.engine.executeSync({
            sql: `SELECT handle_directory_upsert(NULL, ?, ?, ?, 0, ?) AS id;`,
            parameters: [parentId, name, dirPath, versionId],
        }).rows;
        const insertedId = result[0]?.id;
        if (typeof insertedId !== "string") {
            throw new Error(`Failed to create directory for path ${dirPath}`);
        }
        parentId = insertedId;
    }
}
export function ensureDirectoryPathExists(args) {
    if (args.path === "/") {
        return null;
    }
    const normalizedPath = normalizeDirectoryPath(args.path);
    const segments = normalizedPath.slice(1, -1).split("/");
    let parentId = null;
    let currentPath = "";
    for (const segment of segments) {
        const normalizedSegment = normalizePathSegment(segment);
        currentPath += `/${normalizedSegment}`;
        const fullPath = normalizeDirectoryPath(`${currentPath}/`);
        const existing = readDirectoryByPath({
            engine: args.engine,
            versionId: args.versionId,
            path: fullPath,
        });
        if (existing) {
            parentId = existing.id;
            continue;
        }
        const result = args.engine.executeSync({
            sql: `SELECT handle_directory_upsert(NULL, ?, ?, NULL, 0, ?) AS id;`,
            parameters: [parentId, normalizedSegment, args.versionId],
        }).rows;
        const insertedId = result[0]?.id;
        if (typeof insertedId !== "string") {
            throw new Error(`Failed to create directory for path ${fullPath}`);
        }
        parentId = insertedId;
    }
    return parentId;
}
export function composeDirectoryPath(args) {
    if (args.directoryId === null) {
        return "/";
    }
    const segments = [];
    let currentId = args.directoryId;
    let safety = 0;
    while (currentId) {
        if (safety++ > 1024) {
            throw new Error("Directory hierarchy appears to be cyclic");
        }
        const descriptor = readDirectoryDescriptorById({
            engine: args.engine,
            versionId: args.versionId,
            directoryId: currentId,
        });
        if (!descriptor) {
            return undefined;
        }
        segments.push(descriptor.name);
        currentId = descriptor.parent_id ?? null;
    }
    if (segments.length === 0) {
        return "/";
    }
    return `/${segments.reverse().join("/")}/`.normalize("NFC");
}
//# sourceMappingURL=ensure-directories.js.map