import * as _angular_core from '@angular/core';
import { Signal } from '@angular/core';
import { NestedKeyOf, Operator, Filter } from '@sdcorejs/utils/models';
import { MatMenu } from '@angular/material/menu';
import { SdSearch } from '@sdcorejs/angular/forms/models';

type SdQueryFieldType = 'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'values' | 'lazy-values';
interface SdQueryFieldBase<T = any> {
    /** Dot-notation field path (matches `Filter.field`). */
    key: NestedKeyOf<T>;
    /** Human-readable label shown on chip + field picker. */
    label: string;
    /** Material icon name; falls back to `SD_QUERY_TYPE_ICON[type]` then `'tune'`. */
    icon?: string;
    /**
     * Controls the operator selector in the chip popover:
     * - omitted / `false` → **simple mode**: no operator dropdown; the chip always uses
     *   `defaultOperator` (or the per-type default). Use this for screens that don't need
     *   operator choice.
     * - `true` → show the full operator set for the field's `type`
     *   (`SD_QUERY_OPERATORS_BY_TYPE[type]`).
     * - `Operator[]` → show exactly these operators.
     */
    operators?: boolean | Operator[];
    /**
     * Operator the chip starts with. Falls back to `SD_QUERY_DEFAULT_OPERATOR_BY_TYPE[type]`:
     * string→CONTAIN, number→EQUAL, values/lazy-values→IN, boolean→EQUAL, date/datetime→BETWEEN.
     */
    defaultOperator?: Operator;
    /** Field is offered in picker but not interactive (read-only badge in chip). */
    disabled?: boolean;
}
interface SdQueryFieldString<T = any> extends SdQueryFieldBase<T> {
    type: 'string';
}
interface SdQueryFieldNumber<T = any> extends SdQueryFieldBase<T> {
    type: 'number';
    /** Bounds for the input control inside the chip popover. */
    min?: number;
    max?: number;
    /** Step for the number input. */
    step?: number;
}
interface SdQueryFieldBoolean<T = any> extends SdQueryFieldBase<T> {
    type: 'boolean';
    /** Labels rendered for the true/false buttons. Default: "Có" / "Không". */
    trueLabel?: string;
    falseLabel?: string;
}
interface SdQueryFieldDate<T = any> extends SdQueryFieldBase<T> {
    type: 'date' | 'datetime';
    /** Restrict the picker calendar to this range. */
    min?: Date | string;
    max?: Date | string;
}
/**
 * Statically-known options (sync array, signal, or one-shot promise).
 * Mirrors `SdTableColumnValues.option`.
 */
interface SdQueryFieldValues<T = any, K = Record<string, any>> extends SdQueryFieldBase<T> {
    type: 'values';
    option: {
        items: K[] | Signal<K[]> | (() => Promise<K[]>);
        valueField: NestedKeyOf<K>;
        displayField: NestedKeyOf<K>;
    };
}
/**
 * Server-backed options — searchable, paginated. Mirrors `SdTableColumnLazyValues.option`.
 * `search` is the unified `SdSearch<K>` callback used by `sd-select`: it handles
 * `{ type: 'SEARCH', searchText }` for live queries and `{ type: 'VALUE', value }` for
 * resolving chip-display labels of already-selected IDs.
 */
interface SdQueryFieldLazyValues<T = any, K = Record<string, any>> extends SdQueryFieldBase<T> {
    type: 'lazy-values';
    option: {
        search: SdSearch<K>;
        valueField: NestedKeyOf<K>;
        displayField: NestedKeyOf<K>;
    };
}
type SdQueryField<T = any> = SdQueryFieldString<T> | SdQueryFieldNumber<T> | SdQueryFieldBoolean<T> | SdQueryFieldDate<T> | SdQueryFieldValues<T> | SdQueryFieldLazyValues<T>;
type SdQueryLogic = 'AND' | 'OR';
interface SdQuery<T = any> {
    filters: Filter<T>[];
    /** Global connector between filters. Defaults to `'AND'`. */
    logic?: SdQueryLogic;
    /** Free-text search box (only present when `showSearch=true`). */
    search?: string;
}
interface SdQueryBarOption<T = any> {
    /** Prefix for auto-generated `data-autoid` on inner controls. */
    autoId?: string;
    /** Available fields the user can filter by. */
    fields: SdQueryField<T>[];
    /** Initial active filter chips. */
    filters?: Filter<T>[];
    /** Initial global connector between filters. */
    logic?: SdQueryLogic;
    /** Initial free-text search string. */
    search?: string;
    mode?: 'popover' | 'inline';
    density?: 'compact' | 'comfortable';
    showSearch?: boolean;
    showSavedFilters?: boolean;
    savedFiltersKey?: string;
    showLogicToggle?: boolean;
    showClearAll?: boolean;
    showOperatorOnChip?: boolean;
    onQueryChange?: (query: SdQuery<T>) => void;
    onApply?: (query: SdQuery<T>) => void;
}
/** Persisted bookmark of a query (filters + logic + search) — managed by `[savedFiltersKey]`. */
interface SdSavedFilter<T = any> {
    id: string;
    name: string;
    query: SdQuery<T>;
}
/**
 * Transient state of the in-progress chip during inline-mode token build (field →
 * operator → value). Lives at the parent (`SdQueryBar.building()`) and is rendered
 * by `<sd-query-build-chip>`. Step transitions:
 *   - 'operator' — operator menu open, waiting for the user to pick a condition
 *   - 'value'    — operator locked, value picker / seamless input is the focus
 */
interface BuildingChip<T = any> {
    field: SdQueryField<T>;
    operator?: Operator;
    step: 'operator' | 'value';
    value?: unknown;
}
/** Full operator set per field type — used when `operators: true`. */
declare const SD_QUERY_OPERATORS_BY_TYPE: Record<SdQueryFieldType, Operator[]>;
/** Default operator when a chip is created and `field.defaultOperator` is not set. */
declare const SD_QUERY_DEFAULT_OPERATOR_BY_TYPE: Record<SdQueryFieldType, Operator>;
/** Icon fallback per field type when `SdQueryField.icon` is not set. */
declare const SD_QUERY_TYPE_ICON: Record<SdQueryFieldType, string>;
/**
 * Resolves the icon shown on chips and in the field picker.
 * Priority: `field.icon` → `SD_QUERY_TYPE_ICON[field.type]` → `'tune'`.
 */
declare function sdQueryFieldIcon(field: SdQueryField): string;
/** Operators that carry no data — value section is hidden in chip popover. */
declare const SD_QUERY_NO_DATA_OPERATORS: Operator[];
/** Operators that carry an array payload — UI offers multi-select. */
declare const SD_QUERY_MULTI_OPERATORS: Operator[];
/** Picks the initial operator when a new chip is created for `field`. */
declare function sdQueryDefaultOperator(field: SdQueryField): Operator;
/**
 * Operators offered in the chip popover for `field`:
 * - `operators === true` → full set for the type
 * - `operators` is an array → that array (deduped against the type set is the caller's job)
 * - otherwise (simple mode) → just the single default operator (no real choice)
 */
declare function sdQueryAllowedOperators(field: SdQueryField): Operator[];
/**
 * Whether the operator selector should be visible. Simple mode (operators omitted/false,
 * or an array with a single entry) hides the dropdown — the chip uses one fixed operator.
 */
declare function sdQueryShowOperatorSelector(field: SdQueryField): boolean;

/**
 * Popover-mode chip editor — the entire `<mat-menu>` that opens when a popover-mode
 * chip is clicked. Owns ALL operator+value staging signals and async option loading.
 *
 * why: tách khỏi `<sd-query-bar>` để parent chỉ giữ `editingIndex` (chip nào đang
 * mở) + nhận `(commit)` khi popover đóng. Mỗi lần parent gọi `seed(filter, field)`
 * trước khi mở chip popover, child reset staging signals + kích option loading.
 *
 * Public surface (template ref `#cp`):
 *   - `cp.menu()` — the `<mat-menu>` to feed `[menu]` of `<sd-query-popover-chip>`
 *   - `cp.seed(filter, field)` — parent reseeds staging before opening
 *   - `(commit)` output — emitted on mat-menu close with staged Filter
 *   - `(swapField)` output — emitted when the nested field switcher picks a new field
 */
declare class SdQueryChipPopover {
    #private;
    /** Resolved field of the currently editing chip — parent passes from `fieldByKey()`. */
    readonly field: _angular_core.InputSignal<SdQueryField | undefined>;
    /** The chip's current Filter — used by `seed()` for initial staging values. */
    readonly filter: _angular_core.InputSignal<Filter | undefined>;
    /** Optional chip index — embedded in the composed autoIds. */
    readonly chipIndex: _angular_core.InputSignal<number | null>;
    /** Prefix for `data-autoId` on inner controls. */
    readonly autoIdBase: _angular_core.InputSignal<string>;
    /** MatMenu for the field switcher — parent passes its `<sd-query-field-picker>` menu. */
    readonly switchPickerMenu: _angular_core.InputSignal<MatMenu | undefined>;
    /** Emitted when mat-menu closes — parent splices `next` into filters[idx]. */
    readonly commit: _angular_core.OutputEmitterRef<Filter>;
    /** Emitted when nested field-switcher picks a new field — parent calls changeFilterField. */
    readonly swapField: _angular_core.OutputEmitterRef<SdQueryField>;
    /** Staged operator. */
    readonly editingOperator: _angular_core.WritableSignal<Operator>;
    /** Staged value (shape depends on field type + operator). */
    readonly editingValue: _angular_core.WritableSignal<unknown>;
    /** Resolved option list for values / lazy-values. */
    readonly editingOptions: _angular_core.WritableSignal<any[]>;
    /** Loading flag for async option fetch. */
    readonly editingOptionsLoading: _angular_core.WritableSignal<boolean>;
    readonly allowedOperators: _angular_core.Signal<Operator[]>;
    readonly showOperatorSelector: _angular_core.Signal<boolean>;
    /** Whether the staged operator is multi-select (IN/NOT_IN). */
    multiple(op?: Operator): boolean;
    /** Whether the staged operator carries no data (NULL/NOT_NULL). */
    isNoDataOperator(op: Operator | string): boolean;
    /** Compose an autoId for an inner control (`<base>-chip<idx>-<role>`). */
    chipAutoId(role: string): string;
    readonly iconFor: typeof sdQueryFieldIcon;
    readonly menu: _angular_core.Signal<MatMenu | undefined>;
    /** Reseed staging from a new filter/field pair — call before opening the menu. */
    seed(filter: Filter | undefined, field: SdQueryField | undefined): void;
    /** Called on operator change in the popover header — reshapes value when family changes. */
    onEditingOperatorChange(next: Operator): void;
    /** Single-value input change. */
    onEditingValueInput(value: unknown): void;
    /** BETWEEN range — mutate only `.from`. */
    onEditingRangeFrom(value: string | number | null): void;
    /** BETWEEN range — mutate only `.to`. */
    onEditingRangeTo(value: string | number | null): void;
    /** Toggle a value in the staged multi-select array. */
    toggleEditingMultiValue(value: unknown): void;
    isEditingMultiSelected(value: unknown): boolean;
    /**
     * mat-menu's (closed) handler — compose the next Filter from staging signals and
     * emit (commit). Parent splices into its filters[] and nulls editingIndex.
     */
    onMenuClosed(): void;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdQueryChipPopover, never>;
    static ɵcmp: _angular_core.ɵɵComponentDeclaration<SdQueryChipPopover, "sd-query-chip-popover", never, { "field": { "alias": "field"; "required": false; "isSignal": true; }; "filter": { "alias": "filter"; "required": false; "isSignal": true; }; "chipIndex": { "alias": "chipIndex"; "required": false; "isSignal": true; }; "autoIdBase": { "alias": "autoIdBase"; "required": false; "isSignal": true; }; "switchPickerMenu": { "alias": "switchPickerMenu"; "required": false; "isSignal": true; }; }, { "commit": "commit"; "swapField": "swapField"; }, never, never, true, never>;
}

/**
 * Compact popover-mode chip face. Click → opens the parent-supplied [menu] (mat-menu
 * containing the chip editor). Inert div[role=button] so the nested <sd-operator>
 * doesn't end up as a button-in-button (invalid HTML).
 */
declare class SdQueryPopoverChip {
    /** Resolved field (from the bar's `fieldByKey()` map). `undefined` → render raw key. */
    readonly field: _angular_core.InputSignal<SdQueryField | undefined>;
    /** The chip's filter — used to read `field` / `operator` / `data` for display text. */
    readonly filter: _angular_core.InputSignal<Filter>;
    /** "Active" = chip has a real value or is no-data op. Drives layout + colour. */
    readonly active: _angular_core.InputSignal<boolean>;
    /** Operator visible on the chip face (else hidden, ":" separator). */
    readonly showOperator: _angular_core.InputSignal<boolean>;
    /** Rendered value text for the value slot. */
    readonly valueText: _angular_core.InputSignal<string>;
    /** Chip popover mat-menu instance supplied by the parent. */
    readonly menu: _angular_core.InputSignal<MatMenu>;
    /** Fired when the chip popover opens (parent seeds its editing state). */
    readonly open: _angular_core.OutputEmitterRef<void>;
    /** Fired when the user clicks the × removal icon. */
    readonly remove: _angular_core.OutputEmitterRef<void>;
    readonly iconFor: typeof sdQueryFieldIcon;
    filterField(): string;
    filterOperator(): Operator;
    private readonly trigger;
    openMenu(): void;
    closeMenu(): void;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdQueryPopoverChip, never>;
    static ɵcmp: _angular_core.ɵɵComponentDeclaration<SdQueryPopoverChip, "sd-query-popover-chip", never, { "field": { "alias": "field"; "required": false; "isSignal": true; }; "filter": { "alias": "filter"; "required": true; "isSignal": true; }; "active": { "alias": "active"; "required": true; "isSignal": true; }; "showOperator": { "alias": "showOperator"; "required": false; "isSignal": true; }; "valueText": { "alias": "valueText"; "required": false; "isSignal": true; }; "menu": { "alias": "menu"; "required": true; "isSignal": true; }; }, { "open": "open"; "remove": "remove"; }, never, never, true, never>;
}

type Density = 'compact' | 'comfortable';
declare class SdQueryBar {
    #private;
    /** All popover-mode chips in order — used to auto-open after addFilter / close before removeFilter. */
    readonly popoverChips: _angular_core.Signal<readonly SdQueryPopoverChip[]>;
    /** The extracted in-progress chip — owns operator menu + value picker view refs. */
    private readonly buildChip;
    /** The extracted chip popover — owns operator+value staging + commit-on-close. */
    readonly chipPopover: _angular_core.Signal<SdQueryChipPopover | undefined>;
    /** Main option object. Prefer this API for new usage; individual inputs stay as a migration bridge. */
    readonly option: _angular_core.InputSignal<SdQueryBarOption<any> | undefined>;
    /** Prefix for auto-generated `data-autoid` on inner controls (chips, operator/value editors, buttons). */
    readonly autoIdInput: _angular_core.InputSignalWithTransform<string | undefined, string | null | undefined>;
    /** Available fields the user can filter by. Required for the field picker. */
    readonly fields: _angular_core.InputSignalWithTransform<SdQueryField[], SdQueryField[] | null | undefined>;
    /**
     * Active filter chips. Two-way bindable via `[(filters)]`.
     * Use this when you only need a flat `Filter[]` (no global logic / search box).
     */
    readonly filters: _angular_core.ModelSignal<Filter[]>;
    /**
     * Global connector between filters. Two-way bindable via `[(logic)]`.
     * Only meaningful when `showLogicToggle=true` and there are ≥2 filters.
     */
    readonly logic: _angular_core.ModelSignal<SdQueryLogic>;
    /** Free-text search string. Two-way bindable via `[(search)]`. Only shown when `showSearch=true`. */
    readonly search: _angular_core.ModelSignal<string>;
    /**
     * Editing mode:
     * - `'popover'` (default) → compact chips; click a chip to edit operator + value in a
     *   mat-menu popover, commit with "Áp dụng".
     * - `'inline'` → GitLab-style. Each filter's operator + value controls render directly
     *   on the bar (no popup, no per-filter apply). Edits update `filters` live; the user
     *   presses the search button (or Enter) once to fire `(apply)`.
     */
    readonly mode: _angular_core.InputSignalWithTransform<"popover" | "inline", "popover" | "inline" | null | undefined>;
    /** Density preset — chip / control height. */
    readonly density: _angular_core.InputSignalWithTransform<Density, Density | null | undefined>;
    /** Show free-text search input on the left. */
    readonly showSearch: _angular_core.InputSignalWithTransform<boolean, unknown>;
    /** Show saved-filters dropdown on the right. Requires `[savedFiltersKey]` to actually persist. */
    readonly showSavedFilters: _angular_core.InputSignalWithTransform<boolean, unknown>;
    /**
     * Namespace key for persisting saved filters to `localStorage`. When set, the saved-filters
     * dropdown loads/saves under `sd-query-bar:savedFilters:<key>`. Leave undefined to disable.
     */
    readonly savedFiltersKey: _angular_core.InputSignal<string | undefined>;
    /**
     * Snapshot of the current query — fed into `<sd-query-saved-filters-menu [query]>` so
     * saving a filter captures live filters/logic/search.
     */
    readonly currentQuery: _angular_core.Signal<SdQuery<any>>;
    /** Receive an applied filter from the saved-filters menu — re-install bar state + trigger apply. */
    onApplyFilter(saved: SdSavedFilter): void;
    /** Show AND/OR segmented toggle (renders only when there are ≥2 filters). */
    readonly showLogicToggle: _angular_core.InputSignalWithTransform<boolean, unknown>;
    /** Show "Clear all" button when at least one filter is active. */
    readonly showClearAll: _angular_core.InputSignalWithTransform<boolean, unknown>;
    /** Render the operator label on the chip face (default: hidden — operator only visible in popover). */
    readonly showOperatorOnChip: _angular_core.InputSignalWithTransform<boolean, unknown>;
    /** Composite payload — emitted whenever filters / logic / search change. */
    readonly queryChange: _angular_core.OutputEmitterRef<SdQuery<any>>;
    /** Fires when the user presses "Áp dụng" inside a chip popover — host should reload data. */
    readonly apply: _angular_core.OutputEmitterRef<SdQuery<any>>;
    /** Map of `field.key` → `SdQueryField` for fast lookup from filters[]. */
    readonly resolvedAutoId: _angular_core.Signal<string | undefined>;
    readonly resolvedFields: _angular_core.Signal<SdQueryField[]>;
    readonly resolvedMode: _angular_core.Signal<"popover" | "inline">;
    readonly resolvedDensity: _angular_core.Signal<Density>;
    readonly resolvedShowSearch: _angular_core.Signal<boolean>;
    readonly resolvedShowSavedFilters: _angular_core.Signal<boolean>;
    readonly resolvedSavedFiltersKey: _angular_core.Signal<string | undefined>;
    readonly resolvedShowLogicToggle: _angular_core.Signal<boolean>;
    readonly resolvedShowClearAll: _angular_core.Signal<boolean>;
    readonly resolvedShowOperatorOnChip: _angular_core.Signal<boolean>;
    constructor();
    readonly fieldByKey: _angular_core.Signal<Record<string, SdQueryField>>;
    /** Field keys already chained on a chip — the picker greys these out. */
    readonly usedFieldKeys: _angular_core.Signal<Set<string>>;
    /** Render the global AND/OR connector text between chips when `logic === 'OR'`. */
    readonly showOrConnector: _angular_core.Signal<boolean>;
    /** Search button is actionable only when there is something to apply. */
    readonly canSearch: _angular_core.Signal<boolean>;
    /** Index of the chip whose popover is open. Reset to null after commit / close. */
    readonly editingIndex: _angular_core.WritableSignal<number | null>;
    /** Resolved field of the chip whose popover is currently open (or null). */
    readonly editingField: _angular_core.Signal<SdQueryField | null>;
    /**
     * Multi-select is derived from the operator (IN / NOT_IN) — not a field flag.
     * Only called by inline-mode + build-mode templates with a known operator;
     * popover-mode chips read `multiple()` from the child popover directly.
     */
    multiple(op: Operator): boolean;
    readonly iconFor: typeof sdQueryFieldIcon;
    /** Chip is "active" when it has a non-empty value OR a no-data operator (NULL/NOT_NULL). */
    isFilterActive(filter: Filter): boolean;
    /** Rendered value text on a chip. */
    chipValueText(filter: Filter): string;
    isNoDataOperator(op: Operator | string): boolean;
    readonly building: _angular_core.Signal<BuildingChip<any> | null>;
    allowedOperatorsFor(field: SdQueryField): Operator[];
    inlineAutoId(index: number, role: string): string;
    /** Entry point from the field picker — start building a chip for `field`. */
    beginBuild(field: SdQueryField): void;
    /** Operator chosen during build — finish (no-data) or advance to the value step. */
    pickBuildOperator(op: Operator): void;
    /** Value committed during build — push the completed chip, clear building. No emit. */
    commitBuildValue(value: unknown): void;
    /**
     * Commit from the seamless build chip (string / number). An empty value (blur with no
     * input) cancels the build instead of pushing a blank chip.
     */
    onBuildSeamlessCommit(value: unknown): void;
    /** Abandon the in-progress chip. */
    cancelBuild(): void;
    /**
     * Commit both ends of a BETWEEN range at once — called from `<sd-query-inline-chip>`'s
     * `(commitRange)` output when the user picks a date range.
     * why: sd-date-range emits {from,to} via (sdChange) — single call thay cho cặp
     * setFilterRangeFrom / setFilterRangeTo cũ (đã bỏ).
     */
    setFilterRange(i: number, ev: {
        from: unknown;
        to: unknown;
    } | null): void;
    addFilter(field: SdQueryField): void;
    /** Swap the field of an existing chip — reset operator + value to the new field's defaults. */
    changeFilterField(index: number, field: SdQueryField): void;
    updateFilter(index: number, patch: Partial<Filter>): void;
    removeFilter(index: number): void;
    clearAll(): void;
    setLogic(value: SdQueryLogic): void;
    setSearch(value: string): void;
    triggerApply(): void;
    /**
     * Called when a popover-mode chip is clicked — set editingIndex and seed the
     * child popover's staging signals from the chip's Filter.
     */
    openChipPopover(index: number): void;
    /** Receive `(commit)` from the child popover — splice into filters, null the index. */
    onChipPopoverCommit(next: Filter): void;
    static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdQueryBar, never>;
    static ɵcmp: _angular_core.ɵɵComponentDeclaration<SdQueryBar, "sd-query-bar", never, { "option": { "alias": "option"; "required": false; "isSignal": true; }; "autoIdInput": { "alias": "autoId"; "required": false; "isSignal": true; }; "fields": { "alias": "fields"; "required": false; "isSignal": true; }; "filters": { "alias": "filters"; "required": false; "isSignal": true; }; "logic": { "alias": "logic"; "required": false; "isSignal": true; }; "search": { "alias": "search"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "density": { "alias": "density"; "required": false; "isSignal": true; }; "showSearch": { "alias": "showSearch"; "required": false; "isSignal": true; }; "showSavedFilters": { "alias": "showSavedFilters"; "required": false; "isSignal": true; }; "savedFiltersKey": { "alias": "savedFiltersKey"; "required": false; "isSignal": true; }; "showLogicToggle": { "alias": "showLogicToggle"; "required": false; "isSignal": true; }; "showClearAll": { "alias": "showClearAll"; "required": false; "isSignal": true; }; "showOperatorOnChip": { "alias": "showOperatorOnChip"; "required": false; "isSignal": true; }; }, { "filters": "filtersChange"; "logic": "logicChange"; "search": "searchChange"; "queryChange": "queryChange"; "apply": "apply"; }, never, never, true, never>;
}

export { SD_QUERY_DEFAULT_OPERATOR_BY_TYPE, SD_QUERY_MULTI_OPERATORS, SD_QUERY_NO_DATA_OPERATORS, SD_QUERY_OPERATORS_BY_TYPE, SD_QUERY_TYPE_ICON, SdQueryBar, sdQueryAllowedOperators, sdQueryDefaultOperator, sdQueryFieldIcon, sdQueryShowOperatorSelector };
export type { BuildingChip, SdQuery, SdQueryBarOption, SdQueryField, SdQueryFieldBoolean, SdQueryFieldDate, SdQueryFieldLazyValues, SdQueryFieldNumber, SdQueryFieldString, SdQueryFieldType, SdQueryFieldValues, SdQueryLogic, SdSavedFilter };
