import { LixCommitSchema, } from "../commit/schema-definition.js";
import { getTimestamp } from "../engine/functions/timestamp.js";
import { uuidV7 } from "../engine/functions/uuid-v7.js";
import {} from "kysely";
import { LixVersionDescriptorSchema, LixVersionTipSchema, } from "../version/schema-definition.js";
import { LixEntityLabelSchema, } from "../entity/label/schema-definition.js";
/**
 * Converts the current working change set into a checkpoint.
 *
 * The working change set becomes immutable and receives the
 * `checkpoint` label. A fresh empty working change set is created so
 * that new changes can continue to accumulate. Cache tables are
 * updated and a `state_commit` hook fires for observers.
 *
 * @param args.lix - Active Lix instance executing the operation.
 *
 * @example
 * ```ts
 * const { id } = await createCheckpoint({ lix })
 * ```
 */
export async function createCheckpoint(args) {
    const db = args.lix.db;
    const executeInTransaction = async (trx) => {
        // Get current active version
        const activeVersion = await trx
            .selectFrom("active_version")
            .innerJoin("version", "version.id", "active_version.version_id")
            .selectAll("version")
            .executeTakeFirstOrThrow();
        // Get the working commit and its change set
        const workingCommit = await trx
            .selectFrom("commit")
            .where("id", "=", activeVersion.working_commit_id)
            .select(["id", "parent_commit_ids", "change_set_id"])
            .executeTakeFirstOrThrow();
        const workingChangeSetId = workingCommit.change_set_id;
        // Check if there are any working change set elements to checkpoint
        const workingElements = await trx
            .selectFrom("change_set_element_by_version")
            .where("change_set_id", "=", workingChangeSetId)
            .innerJoin("change", "change.id", "change_set_element_by_version.change_id")
            .where("lixcol_version_id", "=", "global")
            .select(["change.id", "change.snapshot_content"])
            .execute();
        if (workingElements.length === 0) {
            // Idempotent behavior: if working set is clean, return the current head commit
            const headCommit = await trx
                .selectFrom("commit_by_version")
                .selectAll()
                .where("id", "=", activeVersion.commit_id)
                .where("lixcol_version_id", "=", "global")
                .executeTakeFirstOrThrow();
            return headCommit;
        }
        // 1) Prepare ids
        const checkpointCommitId = activeVersion.working_commit_id;
        const newWorkingChangeSetId = await uuidV7({ lix: args.lix });
        const newWorkingCommitId = await uuidV7({ lix: args.lix });
        // 2) Ensure checkpoint label exists
        const checkpointLabel = await trx
            .selectFrom("label")
            .where("name", "=", "checkpoint")
            .select("id")
            .executeTakeFirstOrThrow();
        // Update version tip + descriptor via change + cache (avoid version view writes)
        const now = await getTimestamp({ lix: args.lix });
        const descriptorChange = {
            id: await uuidV7({ lix: args.lix }),
            entity_id: activeVersion.id,
            schema_key: LixVersionDescriptorSchema["x-lix-key"],
            schema_version: LixVersionDescriptorSchema["x-lix-version"],
            file_id: "lix",
            plugin_key: "lix_sdk",
            snapshot_content: JSON.stringify({
                id: activeVersion.id,
                name: activeVersion.name,
                inherits_from_version_id: activeVersion.inherits_from_version_id,
                hidden: activeVersion.hidden,
            }),
            created_at: now,
        };
        const tipChange = {
            id: await uuidV7({ lix: args.lix }),
            entity_id: activeVersion.id,
            schema_key: LixVersionTipSchema["x-lix-key"],
            schema_version: LixVersionTipSchema["x-lix-version"],
            file_id: "lix",
            plugin_key: "lix_sdk",
            snapshot_content: JSON.stringify({
                id: activeVersion.id,
                commit_id: checkpointCommitId,
                working_commit_id: newWorkingCommitId,
            }),
            created_at: now,
        };
        const workingDomainChangeIds = workingElements
            .map((r) => r.change_id)
            .filter((id) => Boolean(id));
        // Merge in the previous tip (A) as an additional parent to WC
        const mergedParents = Array.from(new Set([
            ...(workingCommit.parent_commit_ids ?? []),
            activeVersion.commit_id,
        ]));
        // Pre-generate label change id so we can track it in checkpoint change_ids
        const labelChangeId = await uuidV7({ lix: args.lix });
        // Commit change rows (checkpoint + new working)
        const checkpointCommitChange = {
            id: await uuidV7({ lix: args.lix }),
            entity_id: checkpointCommitId,
            schema_key: LixCommitSchema["x-lix-key"],
            schema_version: LixCommitSchema["x-lix-version"],
            file_id: "lix",
            plugin_key: "lix_sdk",
            snapshot_content: JSON.stringify({
                id: checkpointCommitId,
                change_set_id: workingChangeSetId,
                parent_commit_ids: mergedParents,
                change_ids: [...workingDomainChangeIds, labelChangeId],
            }),
            created_at: now,
        };
        const newWorkingCommitChange = {
            id: await uuidV7({ lix: args.lix }),
            entity_id: newWorkingCommitId,
            schema_key: LixCommitSchema["x-lix-key"],
            schema_version: LixCommitSchema["x-lix-version"],
            file_id: "lix",
            plugin_key: "lix_sdk",
            snapshot_content: JSON.stringify({
                id: newWorkingCommitId,
                change_set_id: newWorkingChangeSetId,
                parent_commit_ids: [checkpointCommitId],
                change_ids: [],
            }),
            created_at: now,
        };
        const labelChange = {
            id: labelChangeId,
            entity_id: `${checkpointCommitId}~${checkpointLabel.id}`,
            schema_key: LixEntityLabelSchema["x-lix-key"],
            schema_version: LixEntityLabelSchema["x-lix-version"],
            file_id: "lix",
            plugin_key: "lix_sdk",
            snapshot_content: JSON.stringify({
                entity_id: checkpointCommitId,
                schema_key: "lix_commit",
                file_id: "lix",
                label_id: checkpointLabel.id,
            }),
            created_at: now,
        };
        // Persist changes and update cache without creating a meta-commit
        await trx
            .insertInto("change")
            .values([
            descriptorChange,
            tipChange,
            checkpointCommitChange,
            newWorkingCommitChange,
            labelChange,
        ])
            .execute();
        const materializedChanges = [
            {
                ...descriptorChange,
                lixcol_version_id: "global",
                lixcol_commit_id: checkpointCommitId,
            },
            {
                ...tipChange,
                lixcol_version_id: "global",
                lixcol_commit_id: checkpointCommitId,
            },
            {
                ...checkpointCommitChange,
                lixcol_version_id: "global",
                lixcol_commit_id: checkpointCommitId,
            },
            {
                ...newWorkingCommitChange,
                lixcol_version_id: "global",
                lixcol_commit_id: newWorkingCommitId,
            },
            {
                ...labelChange,
                lixcol_version_id: activeVersion.id,
                lixcol_commit_id: checkpointCommitId,
            },
        ];
        await args.lix.call("lix_update_state_cache", {
            changes: materializedChanges,
        });
        const hookChanges = materializedChanges.map((change) => ({
            id: change.id,
            entity_id: change.entity_id,
            schema_key: change.schema_key,
            schema_version: change.schema_version,
            file_id: change.file_id,
            plugin_key: change.plugin_key,
            created_at: change.created_at,
            snapshot_content: change.snapshot_content
                ? JSON.parse(change.snapshot_content)
                : null,
            metadata: change.metadata ? JSON.parse(change.metadata) : null,
            version_id: change.lixcol_version_id,
            commit_id: change.lixcol_commit_id,
            untracked: 0,
            writer_key: null,
        }));
        // Bridge state_commit so reactive queries observe the new working commit/descriptor state.
        args.lix.hooks._emit("state_commit", { changes: hookChanges });
        // Return the checkpoint commit (old working)
        const createdCommit = await trx
            .selectFrom("commit_by_version")
            .selectAll()
            .where("id", "=", checkpointCommitId)
            .where("lixcol_version_id", "=", "global")
            .executeTakeFirstOrThrow();
        return createdCommit;
    };
    return db.isTransaction
        ? executeInTransaction(db)
        : db.transaction().execute(executeInTransaction);
}
//# sourceMappingURL=create-checkpoint.js.map