import type { ReactNode } from 'react';
import type { PisellLookupProps } from '@pisell/materials';
import type { BaseResponsiveProps } from '../../responsive';
/**
 * 业务域唯一 key
 *
 * 内置 6 个常用 key 仅为提示，业务可自由扩展为任意字符串。
 */
export declare type GlobalSearchProviderKey = 'reservation' | 'order' | 'resource' | 'client' | 'product' | 'device' | (string & {});
/**
 * Provider 多语言标签
 *
 * 使用对象形式而不是 i18n key，是因为 Provider 通常由业务模块自己注册，
 * 业务模块自己持有文案；GlobalSearch 只负责按当前语言挑一条。
 */
export interface GlobalSearchProviderLabel {
    en: string;
    'zh-CN': string;
    'zh-HK': string;
    ja?: string;
    pt?: string;
}
/**
 * 全局搜索的"视角/模式"。
 *
 * - 'admin'    管理模式（如管理员视角，可看全局数据）
 * - 'business' 业务模式（如门店/业务员视角，受业务范围约束）
 *
 * 该值由 PisellGlobalSearch 内的模式单选驱动，作为 search 参数原样传给 Provider；
 * **PisellGlobalSearch 本身不基于它做任何额外逻辑**（不切 tab / 不变 UI / 不过滤 provider）。
 * 是否使用、如何使用，由 Provider.search 的实现决定（接口入参、权限判定等）。
 */
export declare type GlobalSearchMode = 'admin' | 'business';
/**
 * Provider 搜索调用参数
 */
export interface GlobalSearchSearchParams {
    /** 搜索关键词（用户在搜索框输入的文本） */
    keyword: string;
    /** 期望条数；All 模式下通常较小（如 5），单 Tab 模式下较大（如 20） */
    pageSize?: number;
    /** 分页偏移，单 Tab 模式下使用 */
    offset?: number;
    /** 由 GlobalSearch 统一注入的取消信号，Provider 应透传给 fetch/axios */
    signal?: AbortSignal;
    /**
     * 当前所选视角；由 PisellGlobalSearch 的模式单选驱动。
     * Provider 可选择性使用（如拼接到接口请求里）；不读取也完全合规。
     */
    mode: GlobalSearchMode;
    /**
     * 当前搜索作用域：
     * - `'all'`：在「全部」范围内被聚合调度（与其它 Provider 并发）；
     * - `'single'`：在该 Provider 自己的单 tab 内被单独调度。
     *
     * Provider 可据此切换接口（如订单：All 走聚合 `/shop/es/search`、单 tab 走
     * 单模块 `/shop/es/search/sales`）；不读取也完全合规。
     */
    scope: 'all' | 'single';
    /**
     * Provider 可选实时增量回写入口。
     * 仅用于类似订单搜索这种 OS Server delta callback：调用方只会 patch 当前已加载列表中的已有项。
     *
     * @example
     * onRealtimePatch?.([{ order_id: 1001, status: 'paid' }])
     */
    onRealtimePatch?: (items: any[]) => void;
    /** 当前 provider 本轮搜索可复用的实时订阅 id */
    realtimeSubscriberId?: string;
    /** 注册实时订阅清理函数，useProviderSearch 会在重查/卸载时执行 */
    onRealtimeCleanup?: (cleanup: () => void) => void;
}
/**
 * Provider 搜索结果
 */
export interface GlobalSearchSearchResult<TItem = unknown> {
    list: TItem[];
    /** 总数；用于判断是否还有更多以及展示「共 N 条」 */
    total: number;
}
/**
 * Provider renderItem / onSelect 上下文
 */
export interface GlobalSearchItemContext {
    keyword: string;
    /** 关闭搜索面板 */
    close: () => void;
    /** 路由跳转（由宿主注入，例如 history.push / window.location.assign） */
    navigate?: (url: string) => void;
    /**
     * 就地打开详情（opt-in，由 GlobalSearch 注入）。
     *
     * Provider（如订单表格）在自己的行点击里调用它，把「选中了哪条」回传给外层；
     * 外层据此在右侧详情面板（桌面）或抽屉（手机）内联展示详情，而不是跳转 / 开宿主抽屉。
     * 整页形态会注入；其它形态（如 Modal）不注入时为 undefined，Provider 应回退到原有行为。
     */
    onOpenDetail?: (params: {
        providerKey: GlobalSearchProviderKey;
        id: string | number;
        /** 当前行/结果项原始数据；详情组件可用它做首屏预加载，避免再按 id 查询。 */
        record?: unknown;
    }) => void;
    /**
     * Provider 自管实时订阅时，把 delta 原始项同步给外层，用于刷新已打开详情。
     */
    onRealtimePatchItems?: (providerKey: GlobalSearchProviderKey, items: unknown[]) => void;
    /**
     * Provider 自管取数（如管理模式订单表格）时，把当前查询总数回写给外层，
     * 用于分组头「共 N 条」/「View all」展示，从而省去 `search()` 里单独的「只取数量」计数请求。
     */
    onReportTotal?: (providerKey: GlobalSearchProviderKey, total: number) => void;
    /**
     * 详情刷新版本表。实时 delta 命中某条记录时，外层递增对应 key，
     * Provider 可把版本拼进详情组件 key/osKey 触发重新拉取。
     */
    detailRefreshVersions?: Record<string, number>;
}
/**
 * Provider 权限上下文
 */
export interface GlobalSearchPermissionContext {
    user?: unknown;
    permissions?: string[];
    [key: string]: unknown;
}
/**
 * 统一的业务搜索 Provider 接口
 *
 * 每个业务域实现一个 Provider 并注册到 PisellGlobalSearch，
 * 全局搜索本身不感知业务字段、不感知接口。
 */
export interface GlobalSearchProvider<TItem = any> {
    /** 唯一 key；用于 Tab 切换、历史分类等 */
    key: GlobalSearchProviderKey;
    /** Tab 显示名（多语言） */
    label: GlobalSearchProviderLabel;
    /** Tab 图标（可选） */
    icon?: ReactNode;
    /** Tab 排序，从小到大；不传按注册顺序 */
    order?: number;
    /**
     * 搜索实现；必须支持 AbortSignal。
     *
     * GlobalSearch 会保证：
     * - 同一 Provider 的旧请求被 abort 后才发起新请求；
     * - 切换 Tab 时所有 in-flight 请求都会被 abort。
     */
    search: (params: GlobalSearchSearchParams) => Promise<GlobalSearchSearchResult<TItem>>;
    /**
     * 单条结果渲染。
     *
     * - 由业务自己控制卡片样式，保证与业务详情页一致；
     * - 推荐复用 PisellCards 下的卡片；
     * - 不要在这里发请求。
     *
     * 注：当 Provider 同时提供 `renderResults` 时，`renderItem` 会被忽略；
     * 但仍建议保留一个简洁实现作为降级（renderResults 出错时的兜底）。
     */
    renderItem: (item: TItem, ctx: GlobalSearchItemContext) => ReactNode;
    /**
     * 整组结果渲染（可选）。
     *
     * 提供此函数后，PisellGlobalSearch 将**完全交由 Provider** 渲染该业务域的
     * 结果区域（不再使用 renderItem 逐条 map）。适用于：
     *
     * - 想要表格 / 网格 / 自定义 layout 而不是简单列表（如订单复用 PisellSalesManagement）；
     * - 需要 Provider 内部自己管分页 / 过滤 / 选择 / 实时订阅；
     * - 结果区域结构跟"卡片列表"差异很大。
     *
     * 注意事项：
     * - 该函数**完全替换**默认的 loading / empty / list 渲染；
     *   如果你需要这些状态，请自行处理；
   * - 调用方仍然会调用 `search()` 获取 `{ list, total }` 写入 state，
   *   方便分组头展示 "共 N 条"；如果你的 renderResults 不依赖这份 list，
   *   `search` 可以返回 `{ list: [], total: 0 }`（例如业务模式单 tab 由 OrderBusinessList
   *   自管数据，state.total 不可用于该 tab 的结果计数）；
     * - All 模式下仍然会使用 `state.total > list.length` 来决定要不要展示 "View all"，
     *   想关闭这个，只需 `total === list.length`。
     */
    renderResults?: (args: {
        state: GlobalSearchProviderState<TItem>;
        keyword: string;
        /** 当前模式（与 search 参数一致），方便 renderResults 把它继续透传给下游 */
        mode: GlobalSearchMode;
        /**
         * 当前渲染位置：
         * - `'single'`：该 Provider 自己的单 tab（完整结果区，可做无限加载 / 虚拟滚动）；
         * - `'grouped'`：All 模式下的分组预览（通常只展示前若干条 + 「View all」跳转）。
         *
         * Provider 可据此切换重交互形态（如订单业务列表：单 tab 走虚拟滚动 + 触底翻页，
         * 分组里仅做轻量预览）；不读取也完全合规。
         */
        layout: 'single' | 'grouped';
    }, ctx: GlobalSearchItemContext) => ReactNode;
    /**
     * 选中某条结果时触发；用于跳转或就地打开详情 Modal。
     *
     * - 如果在 renderItem 内部已经处理了点击，可以不实现 onSelect。
     * - 推荐写法：业务在 renderItem 里读取 ctx.close / ctx.navigate。
     */
    onSelect?: (item: TItem, ctx: GlobalSearchItemContext) => void;
    /**
     * 是否启用；通常用于权限控制。
     * 返回 false 时不出现在 Tab 列表中。
     */
    enabledWhen?: (ctx: GlobalSearchPermissionContext) => boolean;
    /**
     * 自定义空态文案；不传时使用全局默认。
     */
    emptyText?: GlobalSearchProviderLabel;
}
/**
 * "All" 这个特殊 tab 的 key
 */
export declare const ALL_TAB_KEY: "__all__";
export declare type AllTabKey = typeof ALL_TAB_KEY;
/**
 * 当前激活的 tab key
 */
export declare type GlobalSearchActiveTab = AllTabKey | GlobalSearchProviderKey;
/**
 * 单个 Provider 在 GlobalSearch 内的运行时状态
 */
export interface GlobalSearchProviderState<TItem = unknown> {
    loading: boolean;
    error?: Error;
    list: TItem[];
    total: number;
    /** 最近一次成功搜索对应的 keyword，用于过期结果丢弃 */
    resolvedKeyword?: string;
}
/**
 * 全部 Provider 的运行时状态映射
 */
export declare type GlobalSearchStateMap = Record<GlobalSearchProviderKey, GlobalSearchProviderState>;
/**
 * PisellGlobalSearch Ref
 */
export interface PisellGlobalSearchRef {
    /** 打开搜索面板 */
    open: () => void;
    /** 关闭搜索面板 */
    close: () => void;
    /** 设置关键词（会触发搜索） */
    setKeyword: (keyword: string) => void;
    /** 获取当前关键词 */
    getKeyword: () => string;
    /** 切换 tab */
    setActiveTab: (tab: GlobalSearchActiveTab) => void;
    /** 设置模式（会触发搜索） */
    setMode: (mode: GlobalSearchMode) => void;
    /** 获取当前模式 */
    getMode: () => GlobalSearchMode;
    /** 主动刷新当前 tab */
    refresh: () => void;
}
/**
 * PisellGlobalSearch Props
 *
 * 只暴露与全局搜索相关的参数；其余 PisellLookup 的细节通过 lookupProps 透传。
 */
export interface PisellGlobalSearchProps extends BaseResponsiveProps {
    /**
     * 注册的业务 Provider 列表
     *
     * 通常由业务模块各自提供，再由宿主聚合：
     * ```ts
     * <PisellGlobalSearch providers={[reservation, order, resource, ...]} />
     * ```
     */
    providers: GlobalSearchProvider[];
    /**
     * 权限/用户上下文，传给 Provider.enabledWhen
     */
    permissionContext?: GlobalSearchPermissionContext;
    /**
     * 受控的 activeTab
     */
    activeTab?: GlobalSearchActiveTab;
    /**
     * activeTab 变化回调
     */
    onActiveTabChange?: (tab: GlobalSearchActiveTab) => void;
    /**
     * All 模式下，每个 Provider 展示的最大条数（点击 "View all" 后切到对应 Tab 看完整结果）
     * @default 5
     */
    allModePageSize?: number;
    /**
     * 单 Tab 模式下，每页条数
     * @default 20
     */
    singleModePageSize?: number;
    /**
     * @deprecated 已忽略；组件始终展示 All 范围（与各 Provider 一起露出），
     *   传入此 prop 不再生效，仅为旧 schema/调用方的类型兼容而保留。
     */
    showAllTab?: boolean;
    /**
     * "All" tab 文案
     */
    allTabLabel?: GlobalSearchProviderLabel;
    /**
     * 路由跳转函数；会注入到 Provider 的 ctx.navigate
     */
    navigate?: (url: string) => void;
    /**
     * 默认模式；不传时为 `'business'`。
     */
    defaultMode?: GlobalSearchMode;
    /**
     * 受控的模式。
     */
    mode?: GlobalSearchMode;
    /**
     * 模式变更回调。
     */
    onModeChange?: (mode: GlobalSearchMode) => void;
    /**
     * 是否在结果区顶部显示模式切换单选。
     * @default true
     */
    showModeSwitch?: boolean;
    /**
     * 搜索提交模式：
     *
     * - `'instant'`（默认）：输入即触发搜索，与 PisellLookup 原生行为一致；
     * - `'submit'`：只在用户**按下回车 / 点击搜索按钮**后才把 input 值写入真正的 keyword
     *   并触发搜索。适合"整页全局搜索"这种"准备 + 提交"语义更强的场景。
     *
     * @default 'instant'
     */
    submitMode?: 'instant' | 'submit';
    /**
     * 自定义模式选项的展示文案（覆盖内置 i18n 文案）。
     * 不传时使用内置：管理模式 / 业务模式（中英繁）。
     */
    modeOptions?: {
        admin?: GlobalSearchProviderLabel;
        business?: GlobalSearchProviderLabel;
    };
    /**
     * 透传给底层 PisellLookup 的参数；
     * 用于自定义 trigger / displayType / modalWidth / placeholder / history 等。
     *
     * 注意：onSearch / resultRender / value / onChange 会被 PisellGlobalSearch 接管，
     * 即使传入也会被覆盖。
     */
    lookupProps?: Omit<PisellLookupProps, 'onSearch' | 'resultRender' | 'value' | 'onChange'>;
    /**
     * 自定义类名
     */
    className?: string;
    /**
     * 自定义样式
     */
    style?: React.CSSProperties;
}
