import type { InjectionKey, Ref } from 'vue';

/**
 * 底部浮动面板（基于 Vant `FloatingPanel`）的 props。
 * 对外 v-model：`panel-height`、`panel-anchor`。
 *
 * 事件：`closed` — 关闭动画与遮罩收尾结束（见组件 `defineEmits`）。
 */
export interface FloatPanelProps {
  /** 是否显示遮罩层；为 `false` 时仍占位但背景可透明（配合 `overlayClass`） */
  overlay?: boolean;
  /** 遮罩层额外 class */
  overlayClass?: Array<string | object> | string | object;
  /** 遮罩层内联样式 */
  overlayStyle?: CSSStyleValue;
  /** 点击遮罩是否触发关闭（将 height 置 0） */
  closeOnClickOverlay?: boolean;

  /** 面板容器额外 class */
  containerClass?: Array<string | object> | string | object;
  /** 面板容器内联样式 */
  containerStyle?: CSSStyleValue;

  /** 标题文案（默认 header 插槽） */
  title?: string;
  /**
   * 为 `true` 时在锚点中加入 `maxContainerHeight`，可拖到全屏高度。
   */
  full?: boolean;
  /**
   * 为 `true` 时在锚点中加入 `0`，展示关闭图标，并允许收起到高度 0。
   */
  closable?: boolean;
  /** 是否允许拖拽内容区改变高度（仅头部可拖时为 `false`） */
  contentDraggable?: boolean;
  /**
   * 为 `true` 时按内容高度同步面板总高（测量见 `FloatPanel` 实现）。
   * 不使用 100 / 60% 视口等默认锚点；打开后锚点收敛为 `0`（若 `closable`）、`maxContainerHeight`（若 `full`）与当前内容总高。
   */
  autoHeight?: boolean;
  /**
   * 动画时长（秒）。`0` 表示无过渡。
   * 用于 Vant Overlay / FloatingPanel，并与关闭时 `isClosing` 时长一致。
   */
  duration?: number | string;
  /** 面板允许的最大高度（px），锚点与 autoHeight 裁剪均受此限制 */
  maxContainerHeight?: number;
}

/**
 * 通过 `defineExpose` 暴露给父组件或 `ref` 的实例能力。
 * `setHeight` 的入参为**内容区**高度（不含头部）；默认将总高合并进锚点，见 `setHeight` 的 `updateAnchor`。
 */
export interface FloatPanelInstance {
  /** 当前锚点数组（只读快照） */
  getAnchors(): number[];

  /**
   * 更新锚点集合。
   * @param value - 一个或多个锚点（px）
   * @param reset - `true` 时用 `value` 替换；`false` 时与现有锚点合并
   */
  setAnchors(value: number | number[], reset?: boolean): void;

  /**
   * 按**内容区**高度（不含头）换算为总高度后更新 `panel-height`。
   * @param value - 内容区高度（px）
   * @param updateAnchor - 默认 `true`：将总高合并进锚点（`setAnchors(total, false)`）再设 `height`；
   *   `false` 时仅写入 `height`，不修改锚点列表（可能影响磁力吸附，按需使用）
   */
  setHeight(value: number, updateAnchor?: boolean): void;

  /**
   * 触发关闭动画：立即将 `panel-height` 置 `0`，由 Vant 在 `duration` 内过渡；
   * 同时维持遮罩至动画结束，避免提前卸载打断过渡。
   */
  closeAnimated(): void;

  /**
   * 将总高设为当前**已排序锚点列表里第一个大于 `0` 的项**（跳过关闭态 `0`）。
   * @returns 若无任何 `> 0` 的锚点、或已在目标高度，则为 `false`
   */
  snapMin(): boolean;

  /**
   * 将总高设为当前**已排序锚点列表的最后一个**（通常最大）。
   * @returns 若 `height` 发生变化则为 `true`；无锚点或已在目标高度则为 `false`
   */
  snapMax(): boolean;

  /**
   * 将总高设为 **`maxContainerHeight` 上限**（会合并进锚点，与 `full` 锚点边界一致）。
   * @returns 若 `height` 发生变化则为 `true`；已在该高度则为 `false`
   */
  snapFull(): boolean;

  /** 同 {@link FloatPanelInstance.snapMin}（第一个 `> 0` 的锚点） */
  snapToFirstAnchor(): boolean;

  /** 同 {@link FloatPanelInstance.snapMax} */
  snapToLastAnchor(): boolean;

  /** 同 {@link FloatPanelInstance.snapFull} */
  snapToMaxContainerHeight(): boolean;
}

/**
 * `useFloatPanel` 注入到内容子树的 key，值为指向 `FloatPanelInstance` 的 ref（面板挂载后才有值）。
 */
export const floatPanelContextKey: InjectionKey<Ref<FloatPanelInstance | undefined>> =
  Symbol('floatPanelContext');

/**
 * `useFloatPanel` 返回的操作对象：在 `open` 返回的 Promise 上接收 `complete` / `cancel` 或 `close(result)` 的结果。
 */
export interface FloatPanelApi<P extends Record<string, any>, R = any> extends FloatPanelInstance {
  /**
   * 打开面板并传入内容区 props；返回的 Promise 在关闭时 resolve。
   * @param props - 传给 `Content` 或默认插槽作用域的对象
   */
  open(props: P | void | null): Promise<R | void>;

  /**
   * 关闭面板并 resolve `open` 的 Promise。
   * @param result - 可选结果，会作为 `open` Promise 的 resolve 值
   */
  close(result?: R): void;
}

/**
 * 合并 `open(props)` 与面板注入的关闭回调后，传给内容组件或默认插槽的最终 props。
 *
 * - 内容组件可 `emit('complete', result?)` / `emit('cancel')`
 * - 与直接使用 `onComplete` / `onCancel` 等价（由 `mergeProps` 合并）
 */
export type FloatPanelContentBindings<P extends Record<string, any>, R = unknown> = P & {
  /** 确认关闭，可选传入 `open` Promise 的结果 */
  onComplete: (result?: R) => void;
  /** 取消关闭，`open` Promise resolve 为 `undefined` */
  onCancel: () => void;
};
