"use client";
/**
 * PwaUtil — PWA 工具类
 *
 * 设计要点：
 *   - SSR 安全：所有方法 `typeof window === "undefined"` 守卫，SSR 全部返 false / "unavailable"
 *   - 内部缓存 `beforeinstallprompt` 事件对象；install() 时如有缓存直接 prompt，否则等
 *     事件触发（带超时），超时未到则返 "unavailable"
 *   - install() 用完一次 deferred 后清空 —— 同一事件只能 prompt 一次（浏览器规范）
 *   - 与 `usePwaInstall` hook 并存：hook 用于 React 组件内部监听 state，
 *     本 util 用于业务逻辑（如 ChatArea 想自动触发安装）—— 两者不冲突
 *
 * 已知浏览器差异（2026/06）：
 *   - iOS Safari: 不发 `beforeinstallprompt`，需用户手动 Share → Add to Home Screen，
 *     本 util 在 iOS 上 isSupported()=true 但 install() 永远返 "unavailable"
 *   - 已安装的 PWA: 浏览器不发 `beforeinstallprompt`，isInstalled() 走 standalone 检测
 */

// `beforeinstallprompt` 事件 —— 浏览器非标准但主流都支持。
// 类型参考 MDN: https://developer.mozilla.org/en-US/docs/Web/API/BeforeInstallPromptEvent
interface BeforeInstallPromptEvent extends Event {
  prompt(): Promise<void>;
  userChoice: Promise<{ outcome: "accepted" | "dismissed" }>;
}

/** install() 等待 beforeinstallprompt 事件的最大时长（ms） */
const BEFORE_INSTALL_PROMPT_TIMEOUT_MS = 3000;

export class PwaUtil {
  // 模块级状态（单例内）—— 跨多次 install() 调用共享
  private deferred: BeforeInstallPromptEvent | null = null;
  private listenersAttached = false;

  // ===== 公共 API =====

  /**
   * 检查当前浏览器环境是否支持 PWA 安装。
   *
   * 判定：存在 `window.navigator.serviceWorker`。
   * 注意：这只是"能力"层面的检测；具体能否触发安装还取决于 manifest、
   * HTTPS 上下文、用户参与度（30s+ 互动）等因素 —— 这些由 `install()` 处理。
   */
  isSupported(): boolean {
    if (typeof window === "undefined") return false;
    if (typeof navigator === "undefined") return false;
    return "serviceWorker" in navigator;
  }

  /**
   * 检查 PWA 是否已经以 standalone 模式安装。
   *
   * 判定：
   *   - Android / Chrome: `window.matchMedia("(display-mode: standalone)").matches`
   *   - iOS Safari:       `navigator.standalone === true`
   *
   * 已安装时不应再弹 install 提示。
   */
  isInstalled(): boolean {
    if (typeof window === "undefined") return false;

    // Android / Chrome / Edge: display-mode media query
    if (
      typeof window.matchMedia === "function" &&
      window.matchMedia("(display-mode: standalone)").matches
    ) {
      return true;
    }

    // iOS Safari: 私有属性
    const navAny = navigator as Navigator & { standalone?: boolean };
    if (navAny.standalone === true) {
      return true;
    }

    return false;
  }

  /**
   * 提前挂上 `beforeinstallprompt` 监听。
   *
   * 目的：浏览器事件常常会在页面稳定后自行触发；如果等到用户点"安装"时才挂监听，
   * 很容易错过那次事件，导致后续 `install()` 拿不到 deferred prompt。
   */
  prepareInstallPrompt(): void {
    if (!this.isSupported()) return;
    this.attachListener();
  }

  /** 当前是否已缓存可用的原生安装提示事件。 */
  canPromptInstall(): boolean {
    return this.deferred !== null;
  }

  /**
   * 触发 PWA 安装（浏览器原生安装提示）。
   *
   * 返回值：
   *   - `"accepted"`   用户接受了安装
   *   - `"dismissed"`  用户关闭了提示
   *   - `"unavailable"` 不支持 / 已安装 / 超时未收到 beforeinstallprompt
   *
   * 流程：
   *   1) 不支持 / 已安装 → 直接返 "unavailable"
   *   2) 挂 beforeinstallprompt 监听（懒初始化，挂一次）
   *   3) 如已有 deferred 事件 → 直接 prompt
   *   4) 否则等最多 BEFORE_INSTALL_PROMPT_TIMEOUT_MS，事件到了再 prompt
   *   5) 用完一次 deferred 清空（浏览器规范：同一事件只能 prompt 一次）
   */
  async install(): Promise<"accepted" | "dismissed" | "unavailable"> {
    if (!this.isSupported()) {
      return "unavailable";
    }
    if (this.isInstalled()) {
      return "unavailable";
    }

    // 挂监听（首次 install() 调用时挂上；之后重复调用复用同一 listener）
    this.attachListener();

    // 如已有缓存的 deferred 直接用；否则等事件触发
    if (!this.deferred) {
      const got = await this.waitForDeferred();
      if (!got) return "unavailable";
    }

    const deferred = this.deferred;
    if (!deferred) return "unavailable";
    this.deferred = null; // 一次性 —— 浏览器规范：同一事件只能 prompt 一次

    try {
      await deferred.prompt();
      const choice = await deferred.userChoice;
      return choice.outcome;
    } catch {
      return "unavailable";
    }
  }

  // ===== 私有助手 =====

  /**
   * 挂 beforeinstallprompt 监听（仅首次 install() 调用时挂）
   * - 必须在客户端环境（isSupported() 已 guard）
   * - preventDefault() 阻止浏览器自动弹（让上层控制时机）
   */
  private attachListener(): void {
    if (this.listenersAttached) return;
    if (typeof window === "undefined") return;

    const handler = (e: Event) => {
      // 阻止浏览器默认行为（让应用自己决定何时 prompt）
      e.preventDefault();
      this.deferred = e as BeforeInstallPromptEvent;
    };
    window.addEventListener("beforeinstallprompt", handler);
    this.listenersAttached = true;
  }

  /**
   * 轮询等待 deferred 事件就绪（带超时）
   * - 100ms 间隔轮询，足够响应且不阻塞主线程
   * - 超时返 false —— 调用方走 "unavailable" 兜底
   */
  private waitForDeferred(timeoutMs: number = BEFORE_INSTALL_PROMPT_TIMEOUT_MS): Promise<boolean> {
    return new Promise((resolve) => {
      const start = Date.now();
      const tick = () => {
        if (this.deferred) {
          resolve(true);
          return;
        }
        if (Date.now() - start > timeoutMs) {
          resolve(false);
          return;
        }
        setTimeout(tick, 100);
      };
      tick();
    });
  }
}

/** 全局单例（与 `deviceIdentifier` / `SpAsyncUtil` 命名约定一致） */
export const pwaUtil = new PwaUtil();
