import React from "react";

//#region src/plus/salesSdk/types.d.ts
/** 商品行 identity（OS BaseSalesOrderProductIdentity / ScanOrderOrderProductIdentity 子集） */
interface ScanOrderOrderProductIdentity {
  product_id: number | null;
  product_variant_id: number;
  /** @deprecated 行级唯一值统一使用 metadata.unique_identification_number */
  identity_key?: string;
  unique_identification_number?: string;
  product_option_item?: any[];
  product_bundle?: any[];
}
/** 商品行（OS ScanOrderOrderProduct 结构性子集） */
interface ScanOrderOrderProduct extends ScanOrderOrderProductIdentity {
  order_detail_id: number | null;
  num: number;
  product_option_item: any[];
  selling_price: string;
  original_price: string;
  tax_fee: string;
  is_charge_tax: number;
  discount_list: any[];
  product_bundle: any[];
  metadata: Record<string, any>;
  note?: string;
}
/** OS Order/SalesSummary 汇总结构 */
interface ScanOrderSummary {
  product_quantity: number;
  product_original_amount: string;
  product_amount: string;
  product_expect_amount: string;
  product_tax_fee: string;
  shipping_fee: string;
  shipping_tax_fee: string;
  tax_fee: string;
  surcharge_fee: string;
  discount_amount: string;
  deposit_amount: string;
  expect_amount: string;
  total_amount: string;
  amount_gap: string;
  rounding_amount: string;
  pay_service_charge_amount: string;
  tax_title?: string;
  tax_rate?: number;
  [key: string]: any;
}
/** tempOrder（OS ScanOrderTempOrder 结构性子集） */
interface ScanOrderTempOrder {
  order_id: number | null;
  order_number: string;
  shop_order_number: string;
  shop_full_order_number?: string;
  type: string;
  business_code: string;
  platform: string;
  sales_channel: string;
  order_sales_channel: string;
  status: string;
  payment_status: string;
  shipping_status: string;
  customer_id: number | null;
  customer_name: string;
  country_calling_code: string;
  phone: string;
  email: string;
  is_price_include_tax: number;
  tax_title: string;
  tax_country_code: string;
  currency_code: string;
  currency_symbol: string;
  currency_format: string;
  is_deposit: number;
  deposit_amount: string;
  shop_discount: string;
  surcharge_fee: string;
  note: string;
  schedule_date: string;
  created_at: string;
  products: ScanOrderOrderProduct[];
  bookings: any[];
  payments: any[];
  surcharges: any[];
  discount_list: any[];
  relation_forms: any[];
  contacts: any[];
  /**
   * 联系人信息：与 OS 真源（OrderModule）一致，缺失时为 `null`，有值时必须是对象。
   * 不再使用空数组表示空，提交链路也按 object|null 处理（详见 OS TEMP_ORDER_FIELDS_SUMMARY.md）。
   */
  contacts_info: Record<string, any> | null;
  holder: Record<string, any> | null;
  metadata: Record<string, any>;
  summary?: ScanOrderSummary;
  [key: string]: any;
}
/** 销售详情（来自 parseSalesResponse） */
interface ISalesDetailHeader {
  order_id: number | string | null;
  order_number?: string;
  shop_order_number?: string;
  status?: string;
  payment_status?: string;
  shipping_status?: string;
  customer_id?: number | string | null;
  customer_name?: string;
  country_calling_code?: string;
  phone?: string;
  email?: string;
  metadata?: Record<string, any> | null;
  total_amount?: string | number;
  [key: string]: any;
}
interface ISalesBooking {
  schedule_event_id?: number | null;
  booking_id?: string;
  appointment_status?: string;
  status?: string;
  start_date?: string;
  start_time?: string;
  end_date?: string;
  end_time?: string;
  duration?: number;
  resources?: any[];
  product?: any;
  detail?: any;
  parent_id?: number | null;
  product_uid?: string;
  metadata?: Record<string, any> | null;
  relation_id?: number | string | null;
  [key: string]: any;
}
interface ISalesPayment {
  amount: string;
  origin_amount?: string;
  rounding_amount?: string;
  status?: string;
  payment_time?: string | null;
  type?: string;
  code?: string;
  name?: string;
  [key: string]: any;
}
interface ISalesSurcharge {
  amount?: string;
  type?: string;
  name?: Record<string, string>;
  [key: string]: any;
}
interface ISalesDetailCustomer {
  id: number | string | null;
  name: string;
  phone?: string;
  email?: string;
  country_calling_code?: string;
  [key: string]: any;
}
interface ISalesDetail {
  header: ISalesDetailHeader;
  products: ScanOrderOrderProduct[];
  bookings: ISalesBooking[];
  rootBooking: ISalesBooking | null;
  bookingsByProductUid: Record<string, ISalesBooking[]>;
  payments: ISalesPayment[];
  surcharges: ISalesSurcharge[];
  discount_list: any[];
  customer: ISalesDetailCustomer | null;
  summary: Record<string, any>;
  raw: Record<string, any>;
}
/** OS CustomerModule 选中客户（结构性子集） */
interface ICustomer {
  id: string | number;
  name?: string;
  phone?: string;
  email?: string;
  country_calling_code?: string;
  [key: string]: any;
}
/**
 * OS Order 模块协议字段视图（OrderCustomerView 结构性 alias）。
 * 这些字段会随订单提交到后端；customer_id 与 OS OrderTempOrder 保持 number | null。
 */
interface OrderCustomerView {
  customer_id: number | null;
  customer_name: string;
  phone: string;
  email: string;
  country_calling_code: string;
}
interface IPaginationInfo {
  page: number;
  pageSize: number;
  total: number;
  totalPages: number;
}
interface ICustomerListResponse {
  list: ICustomer[];
  total: number;
  page?: number;
  pageSize?: number;
}
interface IGetCustomerListParams {
  skip?: number;
  num?: number;
  search?: string;
  [key: string]: any;
}
/** 商品列表条目（OS ProductData，UI 只用一小部分字段） */
interface ProductData {
  id: number;
  title?: string;
  cover?: string;
  duration?: number;
  product_resource?: any;
  variants?: any[];
  bundles?: any[];
  options?: any[];
  [key: string]: any;
}
/** OS BookingTicket 加载商品参数 */
interface ILoadProductsParams {
  category_ids?: number[];
  product_ids?: number[];
  collection?: number | string[];
  schedule_date?: string;
  customer_id?: number;
  menu_list_ids?: number[];
  schedule_datetime?: string;
  with_count?: string[];
  with_schedule?: number;
}
interface ILoadProductDetailParams extends Omit<ILoadProductsParams, 'product_ids'> {
  product_id: number;
}
interface IScanResult {
  status: 'success' | 'fail';
  scanType: string;
  scanCode: string;
  result: any;
}
/** Order.updateOrderProduct 入参 */
interface UpdateOrderProductParams {
  product_id: number | null;
  product_variant_id: number;
  updates: Partial<ScanOrderOrderProduct>;
  unique_identification_number?: string;
  /** @deprecated use unique_identification_number / metadata.unique_identification_number */
  identity_key?: string;
  product_option_item?: any[];
  product_bundle?: any[];
  /** 与商品行 1:1 关联的 booking；传则替换 tempOrder.bookings 中对应项（OS updateOrderProduct）。 */
  booking?: Record<string, any>;
}
/** Order.updateOrderProductQuantity 入参；行定位语义与 updateOrderProduct / removeProductFromOrder 一致 */
interface UpdateOrderProductQuantityParams extends ScanOrderOrderProductIdentity {
  num: number;
}
/** @deprecated 使用 UpdateOrderProductParams */
type UpdateProductInOrderParams = UpdateOrderProductParams;
/** OS BaseSales.scanPromotionCode 返回 */
interface BaseSalesScanCodeResult {
  isAvailable: boolean;
  type?: 'server' | string;
  unavailableReason?: string;
}
/** Order rules hooks（结构性，原生类型来自 OS RulesParamsHooks） */
interface RulesParamsHooks {
  getProduct?: (...args: any[]) => any;
  setProduct?: (...args: any[]) => any;
  [key: string]: any;
}
/**
 * OS `LoadSalesDetailParams` 结构性 alias（字段命名与 Order/types 一致，勿自行发明映射）。
 */
interface SalesSdkLoadSalesDetailParams {
  orderId?: number | string;
  orderNumber?: string;
  externalSaleNumber?: string;
  external_sale_number?: string;
  forceRemote?: boolean;
}
/**
 * OS BaseSales.submitSalesOrder 入参（结构性 alias，与 BaseSalesImpl 一致）。
 */
interface SalesSdkSubmitSalesOrderParams {
  payments?: Record<string, any>[];
  paymentStatus?: string;
  smallTicketDataFlag?: number;
  enhancePayload?: (payload: Record<string, any>, ctx: Record<string, any>) => Record<string, any>;
}
/**
 * BookingTicket OS 模块运行时句柄（极简结构性接口；运行时由 `pisellos.getModule(...)` 返回的实例提供）。
 *
 * 对外（消费方）极少直接消费此引用，但 `useSalesSdk().bookingTicket` 仍提供给高级场景。
 * 实际类型由 OS（@pisell/pisellos）提供，这里仅描述本 SDK 调用到的方法。
 */
interface BookingTicketHandle {
  name: string;
  store?: {
    customer?: any;
    order?: any;
    products?: any;
    [key: string]: any;
  };
  effectsOn: (event: string, callback: (payload: any) => void) => () => void;
  loadSalesDetail: (orderIdOrParams: number | string | SalesSdkLoadSalesDetailParams) => Promise<ISalesDetail>;
  refreshSalesDetail: () => Promise<ISalesDetail>;
  /**
   * 更新当前预约的 appointment_status。
   * 数据全部从 OrderModule.tempOrder 取（order_id / metadata / bookings），
   * schedule id 优先 `metadata.appointment_booking_parent_schedule_id`，
   * 否则使用 `parent_id === 0` 的 root booking 的 `schedule_event_id`。
   * 失败时回滚本地 bookings 的 appointment_status 并抛出。
   */
  setBookingStatus: (status: string) => Promise<any>;
  getSalesOrder: () => ISalesDetail | null;
  getOrderHeader: () => ISalesDetailHeader | null;
  getBookings: () => ISalesBooking[];
  getRootBooking: () => ISalesBooking | null;
  getChildBookingsByProductUid: (productUid: string) => ISalesBooking[];
  getPayments: () => ISalesPayment[];
  getSurcharges: () => ISalesSurcharge[];
  getTempOrder: () => ScanOrderTempOrder | null;
  getOrderProducts: () => ScanOrderOrderProduct[];
  getDiscountList: () => any[];
  getSummary: () => Promise<ScanOrderSummary | null>;
  getCart?: () => SalesSdkCartView;
  getCustomer?: () => ICustomer | null;
  getProductCatalog?: () => SalesSdkProductCatalogView;
  addNewOrder: () => Promise<ScanOrderTempOrder>;
  restoreOrder: () => Promise<ScanOrderTempOrder>;
  /**
   * 仅清空 tempOrder 的 products / bookings / discount_list（保留客户、备注等）。
   * OS OrderModule.clearOrderCartLines；编辑态会同步清空内存 salesDetail 对应视图。
   */
  clearOrderCartLines: () => Promise<ScanOrderTempOrder>;
  replaceTempOrder?: (tempOrder: ScanOrderTempOrder, options?: {
    recalculateSummary?: boolean;
    persist?: boolean;
    saveDraft?: boolean;
  }) => Promise<ScanOrderTempOrder>;
  destroy: () => Promise<void>;
  /**
   * 将商品 catalog / 详情选择回调转为 addProductToOrder 入参（OS BaseSales 提供）。
   */
  transformBaseProductToOrderProduct: (params: {
    payload: Record<string, any>;
    fallbackProductId?: number | string | null;
    sourceProduct?: ProductData | Record<string, any> | null;
    sourceItem?: ProductData | Record<string, any> | null;
  }) => (Partial<ScanOrderOrderProduct> & ScanOrderOrderProductIdentity) | null;
  /**
   * OS `OrderModule.addProductToOrder`：把商品行写入 tempOrder.products，
   * 第二参 `booking` 存在时同步生成 tempOrder.bookings 项并通过 `createLinkedProductAndBooking`
   * 自动改写 booking.product_uid / product.booking_uid，保证商品行与 booking 一一对应。
   *
   * `booking` 字段集严格对齐 OS `TEMP_ORDER_FIELDS_SUMMARY.md` Bookings 章节；
   * SDK 端推荐通过 `transformDetailToProductAndBooking` 生成 `(product, booking)` 输入，
   * 不要自行拼装 `_extend` 字段写入这里。
   */
  addProductToOrder: (product: Partial<ScanOrderOrderProduct> & ScanOrderOrderProductIdentity, booking?: Record<string, any>) => Promise<ScanOrderOrderProduct[]>;
  updateOrderProduct: (params: UpdateOrderProductParams) => Promise<ScanOrderOrderProduct[]>;
  updateOrderProductQuantity: (params: UpdateOrderProductQuantityParams) => Promise<ScanOrderOrderProduct[]>;
  removeProductFromOrder: (identity: ScanOrderOrderProductIdentity) => Promise<ScanOrderOrderProduct[]>;
  setOrderProductLineNote: (identity: ScanOrderOrderProductIdentity, note: string) => Promise<ScanOrderOrderProduct[]>;
  updateTempOrderNote: (note: string) => string;
  /**
   * 写入整单优惠金额（tempOrder.shop_discount）。
   * OS 内部会做 Decimal 标准化：负数归零、保留 2 位小数；返回标准化后的字符串。
   */
  updateTempOrderShopDiscount: (amount: string | number) => string;
  /**
   * 写入联系人信息（tempOrder.contacts_info）。
   * OS 内部防御：非对象/数组都归 `null`，只接对象或 null。返回最终写入的对象或 null。
   * 实际由 BaseSalesImpl.updateTempOrderContactsInfo 提供（与 OS Order 模块同名，
   * 与 BaseSales 既有 updateTempOrderNote 命名风格一致）。
   */
  updateTempOrderContactsInfo: (contactsInfo: Record<string, any> | null) => Record<string, any> | null;
  scanPromotionCode: (code: string, customerId?: number) => Promise<BaseSalesScanCodeResult>;
  loadDiscountConfig?: (params?: {
    customerId?: number;
    action?: 'create' | 'edit';
  }) => Promise<{
    productList?: Record<string, any>[];
    discountList?: any[];
  }>;
  bestDiscount?: (cb?: (result: any) => void) => Promise<{
    productList?: Record<string, any>[];
    discountList?: any[];
  }>;
  setDiscountSelected: (params: {
    discountId: number;
    isSelected: boolean;
  }) => Promise<void>;
  submitSalesOrder: <T = any>(params?: SalesSdkSubmitSalesOrderParams) => Promise<T>;
  loadProducts: (params?: ILoadProductsParams, options?: {
    callback?: (r: any) => void;
    subscriberId?: string;
  }) => Promise<ProductData[]>;
  loadProductDetail: (params: ILoadProductDetailParams, options?: {
    callback?: (r: any) => void;
    subscriberId?: string;
  }) => Promise<ProductData | undefined>;
  getProductByIds: (ids: number[]) => Promise<ProductData[]>;
  setActiveCustomer: (customer: ICustomer | null) => void;
  getActiveCustomer: () => ICustomer | null;
  setSelectedCustomerById: (id: string | number) => void;
  getCustomers: () => ICustomer[];
  getCustomerById: (id: string | number) => ICustomer | null;
  clearCustomers: () => void;
  addCustomerToFirst: (customer: ICustomer) => void;
  getCustomerList: (params?: IGetCustomerListParams) => Promise<ICustomerListResponse>;
  changeCustomerPage: (page: number, pageSize?: number) => Promise<ICustomerListResponse>;
  loadMoreCustomers: () => Promise<ICustomerListResponse>;
  resetAndLoadCustomers: (params?: IGetCustomerListParams) => Promise<ICustomerListResponse>;
  /** OS 同步 getter（CustomerModule.getPaginationInfo 暴露），可选；缺失时回退到默认分页 */
  getCustomerPaginationInfo?: () => IPaginationInfo;
  setOrderCustomer?: (customer: ICustomer | null) => void;
  getOrderCustomer?: () => OrderCustomerView | null;
  getOrderCustomerSnapshot?: () => ICustomer | null;
  clearOrderCustomer?: () => void;
  /** 挂载原生扫码枪 / NFC 桥接（global.peripheralsResult）；须在 scanGlobalListener 等之前调用。 */
  initPeripheralsListener?: () => void;
  scanGlobalListener: (cb: (data: IScanResult) => void) => () => void;
  scanCustomerListener: (cb: (data: IScanResult) => void) => () => void;
  scanUniversalListener: (cb: (data: IScanResult) => void, key: string) => () => void;
  activateCamera: (data?: Record<string, any>) => void;
  enableAllScanListeners: () => void;
  disableAllScanListeners: () => void;
  loadBookingConfig?: (params?: Record<string, any>) => Promise<Record<string, any> | null>;
  loadBookingResources?: (params?: {
    date?: string;
    bookingIds?: number[];
  }) => Promise<any[]>;
  initBookingContext?: (params?: {
    bookingConfigParams?: Record<string, any>;
    date?: any;
    loadResources?: boolean;
    bookingIds?: number[];
    forceRefresh?: boolean;
  }) => Promise<{
    bookingConfig: Record<string, any> | null;
    resourcesOrigin: any[];
    resourcesOriginMap: Record<string, any>;
    date: string | null;
  }>;
  /** 是否已具备与入参等价的 BookingContext（命中则 init 不再打接口）。 */
  isBookingContextReady?: (params?: {
    bookingConfigParams?: Record<string, any>;
    date?: any;
    loadResources?: boolean;
    bookingIds?: number[];
  }) => boolean;
  setBookingConfig?: (config: Record<string, any> | null) => void;
  getBookingConfig?: () => Record<string, any> | null;
  setResources?: (resources: any[] | null | undefined) => void;
  getResourcesOrigin?: () => any[];
  getResourcesOriginMap?: () => Record<string, any>;
  setBookingDate?: (date: any) => void;
  getBookingDate?: () => string | null;
  /** 取商品当前可选资源列表（与 info2 `getResourceByIds` 等价，含三特性）。 */
  getResourcesForProduct?: (product: any, options?: {
    extraResources?: Array<{
      form_id: number | string;
      id: number;
    }>;
  }) => any[];
  /** 拼装 cacheItem 的 _extend / _data（资源 / 容量 / timeObj），与 info2 `getProductExtend` 等价。 */
  getProductExtend?: (cacheItem: any, extra?: Record<string, any>) => any;
  /** 取资源不可用原因（结构化 enum + params）。 */
  getResourceErrors?: (resource: any, cacheItem: any) => SalesSdkResourceError[];
  decideAddProduct?: (item: any, options?: Record<string, any>) => {
    action: 'add' | 'requiresDetail' | 'requiresBookingEdit';
    cacheItem?: any;
    payload?: Record<string, any>;
  };
  decideAfterDetail?: (cacheItem: any, options?: Record<string, any>) => {
    action: 'add' | 'requiresBookingEdit';
    cacheItem?: any;
    payload?: Record<string, any>;
  };
  /** @deprecated 请用 decideAddProduct */
  decideAutoClose?: (item: any, options?: Record<string, any>) => {
    autoClose: boolean;
    cacheItem?: any;
  };
  getIsEject?: (item: any, type?: 'select' | 'detail') => 0 | 1;
  getIsOnlySession?: (item: any) => boolean;
  buildRequiresDetailPayload?: (item: any, options?: Record<string, any>) => SalesSdkAddProductRequiresDetailPayload;
  transformDetailToProductAndBooking?: (input: {
    item: any;
    cacheItem?: any;
    detailResult?: {
      e: any;
      extension_type?: any;
      detail?: any;
    };
    options?: Record<string, any>;
  }) => {
    product: Partial<ScanOrderOrderProduct> & ScanOrderOrderProductIdentity;
    booking?: Record<string, any>;
    cacheItem: any;
  };
  /** 已加车 order line + booking → 资源编辑抽屉 cacheItem（OS 官方反查）。 */
  buildCacheItemFromOrderLine?: (input: {
    product: ScanOrderOrderProduct;
    booking: Record<string, any>;
    sourceProduct?: Record<string, any> | null;
  }) => Record<string, any>;
  /** 已加车普通商品 order line → SkuDetailModal edit cacheItem（OS 官方反查）。 */
  buildNormalProductCacheItemFromOrderLine?: (input: {
    product: ScanOrderOrderProduct;
    sourceProduct?: Record<string, any> | null;
  }) => Record<string, any>;
  setOtherParams: (params: Record<string, any>, options?: {
    cover?: boolean;
  }) => Promise<void>;
  getOtherParams: () => Promise<Record<string, any>> | Record<string, any>;
}
/**
 * `cart.addProduct` 返回 `status='requiresDetail'` 时的 payload。
 * UI 拿到后自行打开 Info2（或新版 saleDetail）详情弹窗，弹窗回调后再调 `cart.confirmDetail`。
 */
interface SalesSdkAddProductRequiresDetailPayload {
  item: ProductData | Record<string, any>;
  showConfig: {
    option: boolean;
    session: boolean;
    variant: boolean;
    package: boolean;
    number: boolean;
  };
  isOnlySession: boolean;
  isEject: 0 | 1;
  customerId?: number | string;
  date: string | null;
  productData: ProductData | Record<string, any>;
}
/**
 * `requiresBookingEdit` 时 UI 打开资源 / 时间编辑抽屉所需 payload。
 */
interface SalesSdkRequiresBookingEditPayload {
  cacheItem: any;
  customerId?: number | string;
  date: string | null;
  /**
   * 已加车行「编辑资源」：强制打开抽屉，忽略 cacheItem.autoClose。
   * 加车链路（requiresBookingEdit）不传此字段；autoClose=true 时自动 confirm 跳过抽屉。
   */
  forceOpen?: boolean;
}
/**
 * `cart.addProduct` 判别式返回值。
 *
 * - `added`：两关均通过，已写入 OS tempOrder。
 * - `requiresDetail`：需打开规格 / SKU 弹窗，callback 后调 `cart.confirmDetail`。
 * - `requiresBookingEdit`：规格已就绪，需打开资源 / 时间抽屉，确认后调 `cart.confirmBookingEdit`。
 */
type SalesSdkAddProductResult = {
  status: 'added';
  products: ScanOrderOrderProduct[];
} | {
  status: 'requiresDetail';
  payload: SalesSdkAddProductRequiresDetailPayload;
} | {
  status: 'requiresBookingEdit';
  payload: SalesSdkRequiresBookingEditPayload;
};
/**
 * `cart.confirmDetail` 判别式返回值（规格选完后可能仍需资源编辑）。
 */
type SalesSdkConfirmDetailResult = {
  status: 'added';
  products: ScanOrderOrderProduct[];
} | {
  status: 'requiresBookingEdit';
  payload: SalesSdkRequiresBookingEditPayload;
};
/**
 * `cart.confirmDetail` 输入：UI 拿到 Info2 callback 的 `(e, extension_type, detail)` 后回传。
 */
/** 跨日日期弹窗确认结果（对齐 ticketBooking MultiDayTimeSelectModal callback） */
interface SalesSdkMultiDaySelectResult {
  startDate: string;
  endDate: string;
  duration: number;
}
/** `openMultiDaySelect` Host 入参 */
interface SalesSdkOpenMultiDaySelectPayload {
  productName?: string;
  /** 日历默认锚点日 YYYY-MM-DD */
  defaultStartDate?: string | null;
}
/** `openHolderSelect` Host 入参 */
interface SalesSdkOpenHolderSelectPayload {
  cacheItem: any;
  maxSelectedCount?: number;
  customerId?: number | string;
}
/** 「创建并添加一次性商品」弹窗回传数据（与 booking CustomiseItemModal 字段对齐） */
interface SalesSdkCustomProductInput {
  productName: string;
  vouchersApplicable: boolean;
  priceType: 'sell' | 'deduction';
  priceValue: string;
  quantity: number;
}
interface SalesSdkConfirmDetailInput {
  item: ProductData | Record<string, any>;
  detailResult: {
    e: any;
    extension_type?: any;
    detail?: any;
  };
  options?: {
    quantity?: number;
    notShowToast?: boolean; /** 跨日弹窗确认后的区间（规格弹窗之后注入） */
    multiDayRange?: SalesSdkMultiDaySelectResult;
  };
}
/**
 * `cart.confirmBookingEdit` 输入：资源 / 时间抽屉确认后的 cacheItem。
 */
interface SalesSdkConfirmBookingEditInput {
  cacheItem: any;
  options?: {
    quantity?: number;
    note?: string;
  };
}
/**
 * 页面级 UI Host：由 SalesSdkProvider 注入，供 `cart.addProductWithFlow` 编排弹窗链。
 *
 * 实现方在页面内可用 Hook 组装后传入；Provider 只持有函数引用，不 import 具体弹窗组件。
 */
interface SalesSdkUIHosts {
  /**
   * `requiresDetail` 时打开规格 / SKU / 称重弹窗。
   * 用户取消时返回 `null`。
   */
  openProductDetail?: (payload: SalesSdkAddProductRequiresDetailPayload) => Promise<SalesSdkConfirmDetailInput['detailResult'] | null>;
  /**
   * `requiresBookingEdit` 时打开资源 / 时间编辑抽屉。
   * 用户取消时返回 `null`；确认时返回编辑后的 cacheItem。
   */
  openBookingEdit?: (payload: SalesSdkRequiresBookingEditPayload) => Promise<any | null>;
  /**
   * 规格弹窗确认后、跨日商品须选日期区间（对齐 ticketBooking `MultiDayTimeSelectModal`）。
   */
  openMultiDaySelect?: (payload: SalesSdkOpenMultiDaySelectPayload) => Promise<SalesSdkMultiDaySelectResult | null>;
  /**
   * 加车前 Holder 选择（对齐 ticketBooking `selectHolderModal`）。
   */
  openHolderSelect?: (payload: SalesSdkOpenHolderSelectPayload) => Promise<number[] | null>;
}
/**
 * `cart.addProductWithFlow` 返回值。
 *
 * - `added`：完整链路结束并已写入 OS。
 * - `cancelled`：用户在 Host 弹窗/抽屉中取消。
 * - `requiresDetail` / `requiresBookingEdit`：决策命中但对应 Host 未配置，交由调用方处理。
 */
/** 已加车行「编辑资源」结果 */
type SalesSdkEditCartLineBookingResult = {
  status: 'updated';
  products: ScanOrderOrderProduct[];
} | {
  status: 'cancelled';
};
/** 已加车普通商品行「编辑 SKU」结果 */
type SalesSdkEditCartLineProductResult = SalesSdkEditCartLineBookingResult;
/** 购物车行点击编辑（自动分流 booking / 普通商品） */
type SalesSdkEditCartLineResult = SalesSdkEditCartLineBookingResult;
type SalesSdkAddProductWithFlowResult = {
  status: 'added';
  products: ScanOrderOrderProduct[];
} | {
  status: 'cancelled';
} | {
  status: 'requiresDetail';
  payload: SalesSdkAddProductRequiresDetailPayload;
} | {
  status: 'requiresBookingEdit';
  payload: SalesSdkRequiresBookingEditPayload;
};
/** `cart.addProductWithFlow` 调用参数（含防抖 / Toast） */
interface SalesSdkAddProductWithFlowOptions {
  type?: 'select' | 'detail';
  quantity?: number;
  note?: string;
  /** 直接加车防抖毫秒，默认 100；0 表示不合并、立即加车 */
  debounceMs?: number;
  /** 为 true 时不展示加车 Toast */
  notShowToast?: boolean;
}
/**
 * 资源不可用原因（与 OS `BookingContext.ResourceUnavailableReason` 一比一对齐）。
 *
 * i18n 文案不进 OS，UI 拿到 reason + params 后自行渲染，对照表见 `salesSdk/docs.md`。
 */
type SalesSdkResourceUnavailableReason = 'no_product' | 'time_out_of_range' | 'count_out_of_range' | 'resource_unbound';
interface SalesSdkResourceError {
  reason: SalesSdkResourceUnavailableReason;
  params: {
    labelText?: string;
    startText?: string;
    endText?: string;
    isCrossDay?: boolean;
  };
}
/** SalesSdkProvider 状态机 */
type SalesSdkStatus = 'idle' | 'loading' | 'ready' | 'error';
interface SalesSdkProviderProps {
  /** 多实例区分；与 ticketBooking 共享同一 osKey 时自动复用其 BookingTicket 实例 */
  osKey: string;
  /** 透传给 OS BookingTicket 的 businessCode */
  businessCode?: string;
  /**
   * 拉取 board config 的参数（`/core/board/management/config`），
   * 挂载后由 SalesSdkProvider 调 `bookingTicket.initBookingContext`。
   */
  bookingConfigParams?: Record<string, any>;
  /** 是否在 Provider 挂载后自动初始化 BookingContext，默认 true */
  autoInitBookingContext?: boolean;
  /** 透传给 OS 注册时的 otherParams（platform / type / channel / dineInConfig 等） */
  otherParams?: Record<string, any>;
  /** 业务自定义 rules hooks，否则用 OrderModule 默认 */
  rulesHooks?: RulesParamsHooks;
  /**
   * 给定 orderId 且 autoBootstrap !== false 时，挂载后自动 loadSalesDetail；
   * 不传则不会自动 createNew，避免空 osKey 误建 tempOrder。
   */
  orderId?: number;
  /** 默认 true */
  autoBootstrap?: boolean;
  /**
   * 挂载后是否先 restore 清空 OS 残留 tempOrder / localStorage（与 demo reset 一致），
   * 再 recalcSummary；有 orderId 时在 restore 之后 loadDetail。默认 true。
   * 与 ticketBooking 共享 osKey 且需保留进行中订单时可设为 false。
   */
  autoRestoreOnInit?: boolean;
  /**
   * 页面级弹窗 / 抽屉 Host（规格弹窗、资源编辑等）。
   * 配置后 `cart.addProductWithFlow` 会自动串联；未配置时仍可用 `cart.addProduct` 手动分支。
   */
  uiHosts?: SalesSdkUIHosts;
  /** 是否开启 tempOrder 主副屏同步，默认关闭，由业务入口按需启用。 */
  enableTempOrderSync?: boolean;
  /**
   * 页面/Tab 是否处于激活态（与 ticketBooking isActive 对齐）。
   * 为 true 时挂载或重新激活原生外设扫码桥接；默认 true。
   */
  isActive?: boolean;
  children: React.ReactNode;
}
/** UI 视图：购物车行（多源 enrichment 输出，仅活在 React 内存） */
interface SalesSdkCartItemView extends ScanOrderOrderProduct {
  /** 给 OS removeProductFromOrder/updateOrderProduct 的稳定 identity */
  _identity: ScanOrderOrderProductIdentity;
  _extend: {
    booking: ISalesBooking | null;
  };
  title: string;
  cover: string;
  variant_title: string;
  bundle_titles: string[];
  option_titles: string[];
  note: string;
  duration?: number;
  product_resource?: any;
  capacity?: any;
  start_at: string | null;
  end_at: string | null;
  resource_id?: number | string | null;
}
/** UI 视图：booking 行（平铺，附带关联商品） */
interface SalesSdkCartBookingView extends ISalesBooking {
  _extend: {
    product: SalesSdkCartItemView | null;
  };
}
/** UI 视图：客户卡片（selected → tempOrder fallback） */
interface SalesSdkCustomerView {
  id: number | string;
  name: string;
  phone?: string;
  email?: string;
  country_calling_code?: string;
  cover?: string;
  isWalkIn: boolean;
}
/** UI 视图：BookingTicketImpl.getCart 中 booking 行（商品行挂在 _extend.product） */
interface SalesSdkCartBookingView extends ISalesBooking {
  _extend: {
    product: ScanOrderOrderProduct | null;
  };
}
/** UI 视图：BookingTicketImpl.getCart 输出（与 OS buildCartView 结构一致） */
interface SalesSdkCartView {
  bookings: SalesSdkCartBookingView[];
  items: ScanOrderOrderProduct[];
}
/** UI 视图：BookingTicketImpl.getProductCatalog 输出 */
interface SalesSdkProductCatalogView {
  products: ProductData[];
}
interface SalesSdkContextValue {
  bookingTicket: BookingTicketHandle | null;
  osKey: string;
  status: SalesSdkStatus;
  error: Error | null;
  isReady: boolean;
  shop_full_order_number: string;
  /**
   * 直读 OS tempOrder 原貌（提交契约，UI 不污染）。
   *
   * SDK 顶层维护单一真源；Cart / Customer 等子 Provider 通过 `useSalesSdk()` 共享，
   * 子 Provider 不再重复持有镜像。子 Provider 的写动作（如 cart action）会在末尾
   * 触发内部 RootRefresh 重新读 OS，保证此字段与 OS 状态一致。
   */
  tempOrder: ScanOrderTempOrder | null;
  /**
   * 服务端 sales 详情（编辑态有，新建态为 null）。
   * 来源同 tempOrder，由 SDK 顶层 Provider 维护。
   */
  salesDetail: ISalesDetail | null;
  loadDetail: (orderId: number) => Promise<ISalesDetail>;
  /**
   * 强制走远端拉取销售详情（跳过本地 sqlite 缓存），参数形态与 OS `loadSalesDetail` 一致。
   */
  loadDetailByRemote: (orderId: number) => Promise<ISalesDetail>;
  /**
   * 基于当前 tempOrder.order_id 强制从远端刷新销售详情（转发 OS `refreshSalesDetail`）。
   * 调用前须已 loadDetail / 存在 order_id；成功后同步顶层 `tempOrder` / `salesDetail`。
   * Provider 内部已用 useMemoizedFn 包装，引用稳定。
   */
  refreshSalesDetail: () => Promise<ISalesDetail>;
  /**
   * loadProducts 前确保 boardConfig 已就绪；与 bootstrap `initBookingContext` 同参，
   * 但不强制拉 resources。bootstrap 已完成且 config 命中时会直接跳过。
   */
  ensureBookingConfigForLoad: () => Promise<void>;
  createNew: () => Promise<ScanOrderTempOrder>;
  restore: () => Promise<ScanOrderTempOrder>;
  /**
   * 仅清空购物车商品行（products / bookings / discount_list），保留客户与其它订单字段。
   */
  clearCartItemsOnly: () => Promise<ScanOrderTempOrder>;
  /**
   * 清空所有内容并重置购物车（转发 OS restoreOrder，含清客户）。
   */
  clearCartAndReset: () => Promise<ScanOrderTempOrder>;
  destroy: () => Promise<void>;
  /**
   * 变更当前订单的预约状态，详见 BookingTicketHandle.setBookingStatus。
   * Provider 内部已用 useMemoizedFn 包装，引用稳定，可直接放入 useEffect deps。
   */
  setBookingStatus: (status: string) => Promise<any>;
  /**
   * 写入订单备注（tempOrder.note，字符串）。
   * 直接转发到 OrderModule.updateTempOrderNote（同步赋值），写完触发内部 refreshRootSnapshot
   * 让 React 立即拿到新的 tempOrder.note。返回最终写入的字符串。
   * Provider 内部已用 useMemoizedFn 包装，引用稳定，可直接放入 useEffect deps。
   */
  setOrderNote: (note: string) => string;
  /**
   * 写入整单优惠金额（tempOrder.shop_discount）。
   * 直接转发到 BookingTicket.updateTempOrderShopDiscount（OS 内部 Decimal 标准化：
   * 负数归零、保留 2 位小数），写完触发 refreshRootSnapshot 让 React 立即拿到新值。
   * 返回标准化后的字符串（如 5 -> '5.00'）。
   * Provider 内部已用 useMemoizedFn 包装，引用稳定，可直接放入 useEffect deps。
   */
  setShopDiscount: (amount: string | number) => string;
  /**
   * 写入联系人信息（tempOrder.contacts_info）。
   * 直接转发到 BookingTicket.updateTempOrderContactsInfo（OS 内部防御：非对象/数组归 null），
   * 写完触发 refreshRootSnapshot 让 React 立即拿到新值。返回最终写入的对象或 null。
   * Provider 内部已用 useMemoizedFn 包装，引用稳定，可直接放入 useEffect deps。
   */
  setContactsInfo: (contactsInfo: Record<string, any> | null) => Record<string, any> | null;
  /**
   * 激活扫码/二维码相机入口，直接转发到底层 BookingTicket。
   */
  activateCamera: (data?: Record<string, any>) => void;
  /**
   * 打开结账面板：派发 `pisell1.handleOpenPayment`，
   * `baseSalesModuleName` 自动取本 Provider 的 BookingTicket moduleKey（`getBookingTicketKey(osKey)`），
   * 调用方无需感知模块命名规则。
   *
   * 返回值为底层 action callback 的回传结果，便于消费方在结账完成后做后续动作（如刷新详情）。
   * 当宿主环境未注入 `appHelper.utils.action` 时直接抛错。
   */
  openCheckout: () => Promise<any>;
  /**
   * 读取当前订单折扣列表（转发 OS `BaseSales.getDiscountList`）。
   * 返回克隆数组，避免消费方就地修改污染 OS store。
   */
  getDiscountList: () => any[];
  /**
   * 扫描优惠码（转发 OS `BaseSales.scanPromotionCode`）。
   * 成功后触发顶层 `tempOrder` 快照刷新；返回轻量结果（不含 discountList，需再调 `getDiscountList`）。
   */
  scanPromotionCode: (code: string, customerId?: number) => Promise<BaseSalesScanCodeResult>;
  /**
   * 勾选/取消订单折扣（转发 OS `BaseSales.setDiscountSelected`）。
   * OS 内部会重算 rules、行价与 summary；完成后同步顶层快照。
   */
  setDiscountSelected: (params: {
    discountId: number;
    isSelected: boolean;
  }) => Promise<void>;
}
interface SalesSdkProductsContextValue {
  products: ProductData[];
  loading: boolean;
  error: Error | null;
  load: (params?: ILoadProductsParams, options?: {
    subscriberId?: string;
  }) => Promise<ProductData[]>;
  loadDetail: (params: ILoadProductDetailParams) => Promise<ProductData | undefined>;
  findByIds: (ids: number[]) => Promise<ProductData[]>;
  scanGlobalListener: (cb: (data: IScanResult) => void) => () => void;
  scanCustomerListener: (cb: (data: IScanResult) => void) => () => void;
  scanUniversal: (cb: (data: IScanResult) => void, key: string) => () => void;
  activateCamera: (data?: Record<string, any>) => void;
  enableScan: () => void;
  disableScan: () => void;
}
/** 客户搜索选项：弹窗打开/输入 debounce 等静默刷新时可关闭 Search 按钮 loading */
interface SalesSdkCustomerSearchOptions {
  /** 是否展示 Search 按钮 loading，默认 true */
  showLoading?: boolean;
}
interface SalesSdkCustomerContextValue {
  selected: ICustomer | null;
  list: ICustomer[];
  pagination: IPaginationInfo;
  hasMore: boolean;
  loading: boolean;
  loadingMore: boolean;
  searchParams: Record<string, any>;
  select: (customer: ICustomer | null) => void;
  loadList: (params?: IGetCustomerListParams) => Promise<ICustomerListResponse>;
  search: (keyword: string, options?: SalesSdkCustomerSearchOptions) => Promise<ICustomerListResponse>;
  loadMore: () => Promise<ICustomerListResponse>;
  changePage: (page: number, pageSize?: number) => Promise<ICustomerListResponse>;
  addToFirst: (customer: ICustomer) => void;
  clear: () => void;
  loadById: (id: string | number) => ICustomer | null;
}
interface SalesSdkSummaryContextValue {
  summary: ScanOrderSummary | null;
}
interface SalesSdkCartContextValue {
  /** 直读 OS tempOrder 原貌（提交契约，UI 不污染） */
  tempOrder: ScanOrderTempOrder | null;
  /** 服务端 sales 详情（编辑态有，新建态为 null） */
  salesDetail: ISalesDetail | null;
  discountList: any[];
  /** 平铺 booking + 关联商品（透传 OS） */
  bookings: SalesSdkCartBookingView[];
  /** 未关联 booking 的商品行（透传 OS tempOrder.products） */
  items: SalesSdkCartItemView[];
  /**
   * 加车主入口：走 OS `decideAddProduct` 两阶段决策 → `added` | `requiresDetail` | `requiresBookingEdit`。
   */
  addProduct: (product: ProductData & Record<string, any>, options?: {
    type?: 'select' | 'detail';
    quantity?: number;
    note?: string;
  }) => Promise<SalesSdkAddProductResult>;
  /** 规格弹窗 callback 后回灌；可能返回 `requiresBookingEdit`。 */
  confirmDetail: (input: SalesSdkConfirmDetailInput) => Promise<SalesSdkConfirmDetailResult>;
  /** 资源 / 时间抽屉确认后加车。 */
  confirmBookingEdit: (input: SalesSdkConfirmBookingEditInput) => Promise<{
    products: ScanOrderOrderProduct[];
  }>;
  /**
   * 加车编排入口：内部 `addProduct` → uiHosts 弹窗链 → `confirmDetail` / `confirmBookingEdit`。
   * Host 未配置时遇 `requiresDetail` / `requiresBookingEdit` 会直接返回对应 status。
   */
  addProductWithFlow: (product: ProductData & Record<string, any>, options?: SalesSdkAddProductWithFlowOptions) => Promise<SalesSdkAddProductWithFlowResult>;
  /**
   * 添加自定义（一次性）商品：直接写入 tempOrder.products，不走 decideAddProduct 弹窗链。
   */
  addCustomProduct: (input: SalesSdkCustomProductInput) => Promise<ScanOrderOrderProduct[]>;
  removeItem: (item: SalesSdkCartItemView) => Promise<ScanOrderOrderProduct[]>;
  updateItem: (params: UpdateOrderProductParams) => Promise<ScanOrderOrderProduct[]>;
  /** 仅更新购物车行数量；传入 cart item / booking 行与目标数量即可 */
  updateItemQuantity: (item: SalesSdkCartItemView | SalesSdkCartBookingView, num: number) => Promise<ScanOrderOrderProduct[]>;
  setOrderNote: (note: string) => string;
  setLineNote: (identity: ScanOrderOrderProductIdentity, note: string) => Promise<ScanOrderOrderProduct[]>;
  setDiscount: (params: {
    discountId: number;
    isSelected: boolean;
  }) => Promise<void>;
  scanCode: (code: string, customerId?: number) => Promise<BaseSalesScanCodeResult>;
  submit: <T = any>(params?: SalesSdkSubmitSalesOrderParams) => Promise<T>;
  recalcSummary: () => Promise<ScanOrderSummary | null>;
  /**
   * 已加车预约行编辑资源/时间：OS 反查 cacheItem → openBookingEdit → updateOrderProduct + booking。
   * 无 booking 的行会抛错；未配置 openBookingEdit 时抛错。
   */
  editCartLineBooking: (item: SalesSdkCartItemView) => Promise<SalesSdkEditCartLineBookingResult>;
  /**
   * 已加车普通商品行编辑 SKU：OS 反查 cacheItem → SkuDetailModal → updateOrderProduct（无 booking）。
   */
  editCartLineProduct: (item: SalesSdkCartItemView) => Promise<SalesSdkEditCartLineProductResult>;
  /**
   * 购物车行点击编辑：booking 行反查 items 后，按 `_extend.booking` 分流
   * `editCartLineBooking` / `editCartLineProduct`。
   */
  editCartLine: (target: SalesSdkCartItemView | SalesSdkCartBookingView) => Promise<SalesSdkEditCartLineResult>;
}
//#endregion
export { BaseSalesScanCodeResult, BookingTicketHandle, ICustomer, ICustomerListResponse, IGetCustomerListParams, ILoadProductDetailParams, ILoadProductsParams, IPaginationInfo, ISalesBooking, ISalesDetail, ISalesDetailCustomer, ISalesDetailHeader, ISalesPayment, ISalesSurcharge, IScanResult, ProductData, RulesParamsHooks, SalesSdkAddProductRequiresDetailPayload, SalesSdkAddProductResult, SalesSdkAddProductWithFlowOptions, SalesSdkAddProductWithFlowResult, SalesSdkCartBookingView, SalesSdkCartContextValue, SalesSdkCartItemView, SalesSdkConfirmBookingEditInput, SalesSdkConfirmDetailInput, SalesSdkConfirmDetailResult, SalesSdkContextValue, SalesSdkCustomProductInput, SalesSdkCustomerContextValue, SalesSdkCustomerSearchOptions, SalesSdkCustomerView, SalesSdkEditCartLineBookingResult, SalesSdkEditCartLineProductResult, SalesSdkEditCartLineResult, SalesSdkLoadSalesDetailParams, SalesSdkMultiDaySelectResult, SalesSdkOpenHolderSelectPayload, SalesSdkOpenMultiDaySelectPayload, SalesSdkProductsContextValue, SalesSdkProviderProps, SalesSdkRequiresBookingEditPayload, SalesSdkResourceError, SalesSdkResourceUnavailableReason, SalesSdkStatus, SalesSdkSubmitSalesOrderParams, SalesSdkSummaryContextValue, SalesSdkUIHosts, ScanOrderOrderProduct, ScanOrderOrderProductIdentity, ScanOrderSummary, ScanOrderTempOrder, UpdateOrderProductParams, UpdateOrderProductQuantityParams, UpdateProductInOrderParams };