import { Dispatch, SetStateAction } from 'react';
import { RowHeight } from './row-height.js';
import { DataListDisplayOptions } from './data-list-display-options.js';
import { SortRule, ActiveFilter, ConditionalRule } from './table-properties-types.js';
import { ViewTab } from '../components/templates/list-page.js';
import 'react/jsx-runtime';
import './data-list-view.js';
import './list-page-table-properties.js';
import '../data-list-view-registry-BstmlfQ3.js';

/** Public so hubs that want to clear or namespace-scan storage can. */
declare function lifecycleStorageKey(namespace: string, tabId: string): string;
declare function pageStorageKey(namespace: string): string;
/**
 * Versioned snapshot of a single table's lifecycle state. The fields under
 * `extras` are entity-specific (e.g. placements stuff `conditionalRules` and
 * pagination there) and opaque to this module. Older v1 records (pre-extras
 * rollout) may have those entity-specific fields at the top level — the
 * parser accepts both shapes for back-compat.
 */
interface PersistedLifecycleV1 {
    v: 1;
    sortRules: SortRule[];
    search: string;
    activeFilters: ActiveFilter[];
    filterConnectors: Record<string, "and" | "or">;
    groupBy: string | null;
    colOrder: string[];
    hiddenCols: string[];
    colWidths: Record<string, number>;
    colPins: Record<string, "left" | "right">;
    colWrap: Record<string, boolean>;
    colMenuSearch: Record<string, string>;
    rowHeight: RowHeight;
    showGridlines: boolean;
    filterBarVisible: boolean;
    searchOpen: boolean;
    /** Generic hub-defined extras. Persisted as JSON. */
    extras?: Record<string, unknown>;
    /**
     * @deprecated Legacy top-level fields (used by placements pre-extras
     * rollout). New code SHOULD live under `extras`. Kept here so existing
     * placements `localStorage` payloads still parse.
     */
    conditionalRules?: ConditionalRule[];
    pagination?: boolean;
    paginationPage?: number;
    paginationPageSize?: number;
}
interface PersistedPageV1 {
    v: 1;
    displayOptions: DataListDisplayOptions;
    showMetrics: boolean;
    tabs: ViewTab[];
    activeTabId: string;
}
/**
 * Narrow surface the lifecycle hook needs from `useTableState` — getters +
 * setters for every persisted slice. Defined as a structural type so
 * callers can pass `tableState` directly (it satisfies this shape).
 */
interface TableStatePersistSlice {
    sortRules: SortRule[];
    search: string;
    activeFilters: ActiveFilter[];
    filterConnectors: Record<string, "and" | "or">;
    groupBy: string | null;
    colOrder: string[];
    hiddenCols: Set<string>;
    colWidths: Record<string, number>;
    colPins: Record<string, "left" | "right">;
    colWrap: Record<string, boolean>;
    colMenuSearch: Record<string, string>;
    rowHeight: RowHeight;
    showGridlines: boolean;
    filterBarVisible: boolean;
    searchOpen: boolean;
    setSortRules: Dispatch<SetStateAction<SortRule[]>>;
    setSearch: Dispatch<SetStateAction<string>>;
    setActiveFilters: Dispatch<SetStateAction<ActiveFilter[]>>;
    setFilterConnectors: Dispatch<SetStateAction<Record<string, "and" | "or">>>;
    setGroupBy: Dispatch<SetStateAction<string | null>>;
    setColOrder: Dispatch<SetStateAction<string[]>>;
    setHiddenCols: Dispatch<SetStateAction<Set<string>>>;
    setColWidths: Dispatch<SetStateAction<Record<string, number>>>;
    setColPins: Dispatch<SetStateAction<Record<string, "left" | "right">>>;
    setColWrap: Dispatch<SetStateAction<Record<string, boolean>>>;
    setColMenuSearch: Dispatch<SetStateAction<Record<string, string>>>;
    setRowHeight: Dispatch<SetStateAction<RowHeight>>;
    setShowGridlines: Dispatch<SetStateAction<boolean>>;
    setFilterBarVisible: Dispatch<SetStateAction<boolean>>;
    setSearchOpen: Dispatch<SetStateAction<boolean>>;
}
declare function parsePersistedPage(raw: string | null): PersistedPageV1 | null;
declare function parsePersistedLifecycle(raw: string | null): PersistedLifecycleV1 | null;
/** Column layout only — keeps in-memory search / filters when the column set changes. */
declare function applyLifecycleColumnLayout(ts: TableStatePersistSlice, p: PersistedLifecycleV1, columnKeys: Set<string>): void;
declare function applyLifecyclePersisted(ts: TableStatePersistSlice, p: PersistedLifecycleV1, columnKeys: Set<string>): void;
declare function serializeLifecycle(ts: TableStatePersistSlice, extras?: Record<string, unknown>): PersistedLifecycleV1;
/**
 * Read merged extras from a payload, falling back to the legacy top-level
 * placements fields when `extras` isn't set yet. Generic so hubs can cast to
 * their own extras shape.
 */
declare function readLifecycleExtras<TExtras extends Record<string, unknown>>(p: PersistedLifecycleV1): TExtras | undefined;
declare function loadLifecycleFromStorage(namespace: string, tabId: string): PersistedLifecycleV1 | null;
declare function scheduleLifecycleSave(namespace: string, tabId: string, payload: PersistedLifecycleV1): void;
/**
 * Pre-populate the localStorage lifecycle record for a new `(namespace, tabId)`
 * scope so the next mount of `useTableStateLifecycle` reads back the seeded
 * values on first render.
 *
 * Used by the centralized "Add view" creation flow in `HubTable` — when the
 * user commits a new tab from the creation drawer, the hub client emits a
 * `CreatedViewSpec` describing the filters / sort / columns / etc. that were
 * configured in the ephemeral creation state buffer. Calling this helper with
 * the new `tabId` and the spec's writable slices ensures those values stick
 * when the new tab activates and `useTableStateLifecycle` mounts against that
 * tabId for the first time.
 *
 * Any field omitted falls back to the same defaults `useTableStateLifecycle`
 * uses when reading an empty record (so partial seeding is safe).
 *
 * SSR-safe: no-op on the server (`typeof window === "undefined"`).
 *
 * @example
 * seedTableStateLifecycle("library", "table-abc123", {
 *   activeFilters: spec.filters,
 *   sortRules: spec.sortRules,
 *   colOrder: spec.colOrder,
 *   hiddenCols: spec.hiddenCols,
 *   groupBy: spec.groupBy,
 *   conditionalRules: spec.conditionalRules,
 *   displayOptions: spec.displayOptions,
 * })
 */
interface SeedTableStateLifecyclePartial {
    activeFilters?: ActiveFilter[];
    sortRules?: SortRule[];
    colOrder?: string[];
    /** Pass as an array (the persisted shape). Internal `hiddenCols` is a `Set` but the JSON record stores an array. */
    hiddenCols?: string[];
    groupBy?: string | null;
    conditionalRules?: ConditionalRule[];
    /**
     * Seeded `displayOptions` go into `extras.displayOptions`. `HubTable` reads
     * `extras` via `onLoadExtras` and applies the seeded display options once
     * the new tab mounts.
     */
    displayOptions?: DataListDisplayOptions;
}
declare function seedTableStateLifecycle(namespace: string, tabId: string, partial: SeedTableStateLifecyclePartial): void;
declare function loadPageFromStorage(namespace: string): PersistedPageV1 | null;
declare function schedulePageSave(namespace: string, payload: PersistedPageV1): void;
interface UseTableStateLifecycleOptions<TExtras extends Record<string, unknown> | void = void> {
    /** Storage namespace, e.g. `"placements"`, `"team"`, `"library"`. */
    namespace: string;
    /**
     * Sub-key per lifecycle tab. A hub with only one lifecycle should pass a
     * stable constant like `"main"`. A hub with multiple lifecycle scopes
     * (e.g. placements' "all / mine / shared" tabs) passes the active scope id.
     */
    tabId: string;
    /** `useTableState(...)` return value. Satisfies `TableStatePersistSlice`. */
    tableState: TableStatePersistSlice;
    /**
     * Valid column keys for the active table. Persisted column references that
     * are no longer present (e.g. column was renamed / removed) are dropped on
     * load.
     */
    columnKeys: Set<string>;
    /**
     * Current value of extra state to persist alongside the table (optional).
     * Pass `undefined` / omit entirely if the hub only persists the table.
     */
    extras?: TExtras;
    /**
     * Called once when the persisted record is loaded, with whatever `extras`
     * it contained (or legacy top-level fields). Use this to rehydrate the
     * matching React state in the consumer.
     */
    onLoadExtras?: (extras: TExtras | Record<string, unknown> | undefined) => void;
}
/**
 * Opt-in lifecycle persistence for a `DataTable`. Wires up:
 *
 * 1. **Load** (`useLayoutEffect`, once per `tabId` / `columnKeys` change) —
 *    reads from `localStorage` and pushes the persisted state back into
 *    `tableState` setters; then calls `onLoadExtras` so the consumer can
 *    restore hub-specific state too.
 * 2. **Save** (`useEffect`, debounced ~400ms) — re-serializes whenever any
 *    persisted slice changes and writes to `localStorage`.
 *
 * Behaviour:
 *
 * - SSR-safe (`localStorage` reads are guarded; the layout effect only runs
 *   on the client).
 * - No render hits: setters are called inside the effect, not during render.
 * - Doesn't depend on the full `tableState` object — it depends on each
 *   persisted slice individually so the table object identity (which is fresh
 *   every render) doesn't force re-saves.
 */
declare function useTableStateLifecycle<TExtras extends Record<string, unknown> | void = void>(opts: UseTableStateLifecycleOptions<TExtras>): void;

export { type PersistedLifecycleV1, type PersistedPageV1, type SeedTableStateLifecyclePartial, type TableStatePersistSlice, type UseTableStateLifecycleOptions, applyLifecycleColumnLayout, applyLifecyclePersisted, lifecycleStorageKey, loadLifecycleFromStorage, loadPageFromStorage, pageStorageKey, parsePersistedLifecycle, parsePersistedPage, readLifecycleExtras, scheduleLifecycleSave, schedulePageSave, seedTableStateLifecycle, serializeLifecycle, useTableStateLifecycle };
