import { getStateCacheTables } from "./schema.js";
import { createSchemaCacheTable, schemaKeyToCacheTableName, } from "./create-schema-cache-table.js";
import { resolveCacheSchemaDefinition } from "./schema-resolver.js";
import { LixStoredSchemaSchema } from "../../stored-schema/schema-definition.js";
import { getAllStoredSchemas } from "../../stored-schema/get-stored-schema.js";
/**
 * Populates the state cache v2 from the materializer view.
 *
 * This function reads from the materialized state and writes to the per-schema
 * physical cache tables. If a version_id is provided, it also populates the
 * cache for all ancestor versions.
 *
 * @param lix - The Lix instance with sqlite and db
 * @param options - Optional filters for selective population
 */
export function populateStateCache(args) {
    const { sqlite } = args.engine;
    const options = args.options ?? {};
    let versionsToPopulate;
    if (options.version_id) {
        // When a specific version is requested, also include all its ancestors
        // This ensures the resolved view can access inherited state
        // Use the materializer's version ancestry view to find all ancestors
        const ancestorRows = sqlite.exec({
            sql: `
				SELECT DISTINCT ancestor_version_id as version_id
				FROM lix_internal_materialization_version_ancestry
				WHERE version_id = ?
			`,
            bind: [options.version_id],
            returnValue: "resultRows",
            rowMode: "array",
        });
        // The ancestry view includes the version itself and all its ancestors
        versionsToPopulate =
            ancestorRows.length > 0
                ? ancestorRows.map((row) => row[0])
                : [options.version_id];
    }
    else {
        // If no version_id specified, populate all active versions (with tips)
        const tipRows = sqlite.exec({
            sql: `SELECT version_id FROM lix_internal_materialization_version_tips`,
            returnValue: "resultRows",
            rowMode: "array",
        });
        versionsToPopulate = tipRows.map((row) => row[0]);
    }
    if (versionsToPopulate.length === 0) {
        return;
    }
    // Prime the stored schema cache so lookups succeed while rebuilding tables.
    getAllStoredSchemas({ engine: args.engine });
    // Clear existing cache entries for the versions being populated
    const tableCache = getStateCacheTables({ engine: args.engine });
    for (const tableName of tableCache) {
        if (tableName === "lix_internal_state_cache")
            continue;
        const tableExists = sqlite.exec({
            sql: `SELECT 1 FROM sqlite_schema WHERE type='table' AND name=?`,
            bind: [tableName],
            returnValue: "resultRows",
        });
        if (tableExists && tableExists.length > 0) {
            const placeholders = versionsToPopulate.map(() => "?").join(",");
            sqlite.exec({
                sql: `DELETE FROM ${tableName} WHERE version_id IN (${placeholders})`,
                bind: versionsToPopulate,
            });
        }
    }
    // Query materialized state to get changes for all required versions
    const placeholders = versionsToPopulate.map(() => "?").join(",");
    const selectSql = `
		SELECT 
			m.entity_id,
			m.schema_key,
			m.file_id,
			m.version_id,
			m.plugin_key,
			m.snapshot_content,
			m.schema_version,
			m.created_at,
			m.updated_at,
			m.change_id,
			m.commit_id,
			m.inherited_from_version_id
		FROM lix_internal_state_materializer m
		WHERE m.version_id IN (${placeholders})
		  AND m.inherited_from_version_id IS NULL
	`;
    const results = sqlite.exec({
        sql: selectSql,
        bind: versionsToPopulate,
        returnValue: "resultRows",
        rowMode: "object",
    });
    if (!results || results.length === 0) {
        return;
    }
    // Group results by schema_key for batch processing
    const rowsBySchema = new Map();
    for (const row of results) {
        if (!rowsBySchema.has(row.schema_key)) {
            rowsBySchema.set(row.schema_key, []);
        }
        rowsBySchema.get(row.schema_key).push(row);
    }
    // Process each schema's rows directly to its physical table
    const schemaEntries = Array.from(rowsBySchema.entries());
    const storedSchemaKey = LixStoredSchemaSchema["x-lix-key"];
    schemaEntries.sort((a, b) => {
        if (a[0] === storedSchemaKey)
            return -1;
        if (b[0] === storedSchemaKey)
            return 1;
        return 0;
    });
    for (const [schema_key, schemaRows] of schemaEntries) {
        const schemaDefinition = resolveCacheSchemaDefinition({
            engine: args.engine,
            schemaKey: schema_key,
        });
        if (!schemaDefinition) {
            throw new Error(`populateStateCache: missing stored schema for ${schema_key}`);
        }
        const tableName = createSchemaCacheTable({
            engine: args.engine,
            schema: schemaDefinition,
        });
        const tableCache = getStateCacheTables({ engine: args.engine });
        if (!tableCache.has(tableName)) {
            tableCache.add(tableName);
        }
        const sanitized = schemaKeyToCacheTableName(schema_key);
        if (!tableCache.has(sanitized)) {
            tableCache.add(sanitized);
        }
        // Batch insert with prepared statement
        const stmt = sqlite.prepare(`
			INSERT INTO ${tableName} (
				entity_id,
				schema_key,
				file_id,
				version_id,
				plugin_key,
				snapshot_content,
				schema_version,
				created_at,
				updated_at,
				inherited_from_version_id,
				is_tombstone,
				change_id,
				commit_id
			) VALUES (?, ?, ?, ?, ?, jsonb(?), ?, ?, ?, ?, ?, ?, ?)
			ON CONFLICT(entity_id, file_id, version_id) DO UPDATE SET
				schema_key = excluded.schema_key,
				plugin_key = excluded.plugin_key,
				snapshot_content = excluded.snapshot_content,
				schema_version = excluded.schema_version,
				-- Preserve both timestamps exactly as they are from the materializer
				created_at = excluded.created_at,
				updated_at = excluded.updated_at,
				inherited_from_version_id = excluded.inherited_from_version_id,
				is_tombstone = excluded.is_tombstone,
				change_id = excluded.change_id,
				commit_id = excluded.commit_id
		`);
        try {
            for (const row of schemaRows) {
                const isDeletion = row.snapshot_content === null || row.snapshot_content === undefined;
                stmt.bind([
                    row.entity_id,
                    row.schema_key,
                    row.file_id,
                    row.version_id,
                    row.plugin_key,
                    row.snapshot_content, // jsonb() conversion happens in SQL
                    row.schema_version,
                    row.created_at, // Preserve original created_at
                    row.updated_at, // Preserve original updated_at
                    row.inherited_from_version_id,
                    isDeletion ? 1 : 0, // is_tombstone
                    row.change_id,
                    row.commit_id,
                ]);
                stmt.step();
                stmt.reset();
            }
        }
        finally {
            stmt.finalize();
        }
    }
}
//# sourceMappingURL=populate-state-cache.js.map