import type { FdoDecimal } from '@feedmepos/core/entity';
export interface BindingSelector {
    type: FdoInventoryBinding['type'];
    id: string;
    amount?: FdoDecimal;
    measurementId?: string | null;
    rules?: Record<string, any> | null;
    alternatives?: FdoInventoryBindingAlternative[];
}
export type BindingChange = {
    type: 'replace-binding';
    from: BindingSelector;
    to: FdoInventoryBinding;
} | {
    type: 'remove-binding';
    target: BindingSelector;
} | {
    type: 'add-binding';
    binding: FdoInventoryBinding;
} | {
    type: 'update-binding-rule';
    target: BindingSelector;
    rule: Record<string, any> | null;
} | {
    type: 'update-alternatives';
    target: BindingSelector;
    alternatives: FdoInventoryBindingAlternative[];
};
export type RecipeChangeOutcome = 'applied' | 'ignored' | 'already-satisfied' | 'blocked';
export interface RecipeChangeResult {
    change: BindingChange;
    outcome: RecipeChangeOutcome;
    reason?: string;
}
export type RecipeBulkEditEntryStatus = 'will-update' | 'already-satisfied' | 'partially-ignored' | 'blocked' | 'no-applicable-changes';
export interface RecipeBulkEditEntry {
    recipeId: string;
    recipeName: string;
    original: FdoInventoryRecipe;
    projected: FdoInventoryRecipe | null;
    status: RecipeBulkEditEntryStatus;
    changes: RecipeChangeResult[];
    counts: {
        applied: number;
        ignored: number;
        blocked: number;
        satisfied: number;
    };
}
export type FlatBinding = FdoInventoryBinding & {
    recipeIds: string[];
    isGroup?: boolean;
};
export declare function flattenBindings(bindings: FdoInventoryBinding[], visitedIds: Set<string>, recipeById: Record<string, FdoInventoryRecipe>): FlatBinding[];
export declare function decimalsEqual(a: FdoDecimal, b: FdoDecimal): boolean;
/**
 * Normalise a binding's `rules` for comparison.
 *
 * `FmCustomAttributeFilter` emits `{ $and: [] }` on mount for falsy values,
 * causing spurious diffs. null / {} / { $and: [] } / { $or: [] } are all
 * treated as "no rules" and normalised to `null`.
 */
export declare function normaliseRules(rules: Record<string, any> | null | undefined): string | null;
export declare function normaliseAlternatives(alts: FdoInventoryBindingAlternative[] | null | undefined): string;
export declare function toBaseAmount(b: FdoInventoryBinding): FdoDecimal;
export declare function bindingsExactlyEqual(a: FdoInventoryBinding, b: FdoInventoryBinding): boolean;
export declare function bindingsSimilar(a: FdoInventoryBinding, b: FdoInventoryBinding): boolean;
export declare function simulateChanges(original: FdoInventoryRecipe, changes: BindingChange[]): {
    projected: FdoInventoryRecipe;
    results: RecipeChangeResult[];
};
/**
 * Returns true if a BindingChange is fully specified (all required IDs are
 * non-empty). Stub/incomplete cards (e.g. a freshly-added blank tab) should be
 * filtered out before being passed to simulateChanges.
 */
export declare function isCompleteChange(change: BindingChange): boolean;
export declare function deriveEntryStatus(results: RecipeChangeResult[]): RecipeBulkEditEntryStatus;
export declare function deriveChangesFromDiff(original: FdoInventoryRecipe, draft: FdoInventoryRecipe): BindingChange[];
