import { columnReference, identifier, } from "../sql-parser/nodes.js";
import { resolveEntityView, resolveMetadataDefaults, normalizeOverrideValue, rewriteViewWhereClause, combineWithAnd, collectPointerColumnDescriptors, extractPrimaryKeys, collectPrimaryKeyEqualityExpressions, buildEntityIdExpressionFromEquality, } from "./shared.js";
import { rewriteSelectStatementTree } from "./select.js";
import { normalizeSegmentedStatement } from "../sql-parser/parse.js";
export const rewriteEntityViewDelete = (context) => {
    const storedSchemas = context.getStoredSchemas?.();
    if (!storedSchemas || storedSchemas.size === 0) {
        return context.statements;
    }
    let anyChanges = false;
    const rewrittenStatements = context.statements.map((statement) => {
        const rewritten = rewriteSegmentedStatement(statement, context, storedSchemas);
        if (rewritten !== statement) {
            anyChanges = true;
        }
        return rewritten;
    });
    return anyChanges ? rewrittenStatements : context.statements;
};
function rewriteSegmentedStatement(statement, context, storedSchemas) {
    let changed = false;
    const segments = statement.segments.map((segment) => {
        if (segment.node_kind !== "delete_statement") {
            return segment;
        }
        const rewritten = rewriteDeleteSegment({
            statement: segment,
            context,
            storedSchemas,
        });
        if (!rewritten) {
            return segment;
        }
        if (rewritten !== segment) {
            changed = true;
        }
        return rewritten;
    });
    if (!changed) {
        return statement;
    }
    return normalizeSegmentedStatement({
        ...statement,
        segments,
    });
}
function rewriteDeleteSegment(args) {
    const viewName = extractTableName(args.statement.target);
    if (!viewName) {
        return null;
    }
    const resolved = resolveEntityView({
        storedSchemas: args.storedSchemas,
        viewName,
    });
    if (!resolved) {
        return null;
    }
    const { schema, variant, storedSchemaKey, propertyLowerToActual } = resolved;
    const rewritten = buildEntityViewDelete({
        statement: args.statement,
        schema,
        variant,
        storedSchemaKey,
        propertyLowerToActual,
        celEnvironment: args.context.getCelEnvironment?.() ?? null,
        context: args.context,
        storedSchemas: args.storedSchemas,
    });
    if (!rewritten) {
        return null;
    }
    args.context.trace?.push({
        step: "rewrite_entity_view_delete",
        payload: {
            view: viewName,
            schema: schema["x-lix-key"],
            variant,
        },
    });
    return rewritten;
}
function buildEntityViewDelete(args) {
    const { statement, schema, variant, storedSchemaKey, propertyLowerToActual, celEnvironment, } = args;
    const primaryKeys = extractPrimaryKeys(schema);
    const entityIdMatch = collectPrimaryKeyEqualityExpressions({
        whereClause: statement.where_clause,
        primaryKeys,
    });
    const entityIdExpressionSql = buildEntityIdExpressionFromEquality(entityIdMatch);
    const entityIdCondition = entityIdExpressionSql
        ? createBinaryExpression(columnReference(["state_by_version", "entity_id"]), createRawExpression(entityIdExpressionSql))
        : null;
    const pointerDescriptors = collectPointerColumnDescriptors({ schema });
    const pointerAliasToPath = new Map(pointerDescriptors.map((descriptor) => [
        descriptor.alias.toLowerCase(),
        descriptor.path,
    ]));
    const pointerSet = new Set(pointerAliasToPath.keys());
    const rewrite = rewriteViewWhereClause(statement.where_clause, {
        propertyLowerToActual,
        stateTableAlias: "state_by_version",
        pointerAliasToPath,
        pushdownableJsonProperties: pointerSet,
        rewriteSubqueryStatement: (subquery) => rewriteSelectStatementTree(subquery, args.context, args.storedSchemas),
    });
    if (!rewrite) {
        return null;
    }
    let { expression: rewrittenWhere, hasVersionReference } = rewrite;
    const overrides = resolveMetadataDefaults({
        defaults: schema["x-lix-override-lixcols"],
        cel: celEnvironment,
        context: {},
    });
    const extraConditions = [
        createBinaryExpression(columnReference(["state_by_version", "schema_key"]), createLiteralExpression(storedSchemaKey)),
    ];
    if (entityIdCondition) {
        extraConditions.push(entityIdCondition);
    }
    const versionOverride = overrides.has("lixcol_version_id")
        ? overrides.get("lixcol_version_id")
        : undefined;
    if (!hasVersionReference) {
        if (variant === "base") {
            const versionExpression = versionOverride !== undefined
                ? createLiteralFromOverride(versionOverride)
                : createActiveVersionSubquery();
            extraConditions.push(createBinaryExpression(columnReference(["state_by_version", "version_id"]), versionExpression));
            hasVersionReference = true;
        }
        else if (variant === "by_version") {
            if (versionOverride === undefined) {
                throw new Error(`DELETE from ${storedSchemaKey}_by_version requires explicit lixcol_version_id or schema default`);
            }
            const versionExpression = createLiteralFromOverride(versionOverride);
            extraConditions.push(createBinaryExpression(columnReference(["state_by_version", "version_id"]), versionExpression));
        }
    }
    let finalWhere = rewrittenWhere;
    for (const condition of extraConditions) {
        finalWhere = finalWhere ? combineWithAnd(finalWhere, condition) : condition;
    }
    return {
        node_kind: "delete_statement",
        target: buildTableReference("state_by_version"),
        where_clause: finalWhere ?? null,
    };
}
function buildTableReference(name) {
    return {
        node_kind: "table_reference",
        name: buildObjectName(name),
        alias: null,
    };
}
function buildObjectName(name) {
    return {
        node_kind: "object_name",
        parts: [identifier(name)],
    };
}
function createBinaryExpression(left, right) {
    return {
        node_kind: "binary_expression",
        left,
        operator: "=",
        right,
    };
}
function createLiteralExpression(value) {
    return {
        node_kind: "literal",
        value,
    };
}
function createRawExpression(sql) {
    return {
        node_kind: "raw_fragment",
        sql_text: sql,
    };
}
function createLiteralFromOverride(value) {
    const normalized = normalizeOverrideValue(value);
    if (typeof normalized === "string" ||
        typeof normalized === "number" ||
        typeof normalized === "boolean" ||
        normalized === null) {
        return createLiteralExpression(normalized);
    }
    return createLiteralExpression(String(normalized));
}
function createActiveVersionSubquery() {
    const versionColumn = columnReference(["version_id"]);
    return {
        node_kind: "subquery_expression",
        statement: {
            node_kind: "select_statement",
            distinct: false,
            projection: [
                {
                    node_kind: "select_expression",
                    expression: versionColumn,
                    alias: null,
                },
            ],
            from_clauses: [
                {
                    node_kind: "from_clause",
                    relation: {
                        node_kind: "table_reference",
                        name: buildObjectName("active_version"),
                        alias: null,
                    },
                    joins: [],
                },
            ],
            where_clause: null,
            group_by: [],
            order_by: [],
            limit: null,
            offset: null,
            with_clause: null,
        },
    };
}
function extractTableName(target) {
    const parts = target.name.parts;
    if (parts.length === 0) {
        return null;
    }
    return parts[parts.length - 1]?.value ?? null;
}
//# sourceMappingURL=delete.js.map