"use client";
/**
 * PwaUtil — PWA 工具类
 *
 * 设计要点：
 *   - SSR 安全：所有方法 `typeof window === "undefined"` 守卫，SSR 全部返 false / "unavailable"
 *   - 只支持可触发原生 `beforeinstallprompt` 的平台 / 浏览器组合
 *   - 内部缓存 `beforeinstallprompt` 事件对象；install() 时没有缓存则直接返 "unavailable"
 *   - install() 用完一次 deferred 后清空 —— 同一事件只能 prompt 一次（浏览器规范）
 *
 * 浏览器白名单对齐 Flutter 侧 pwa_utils.dart：
 *   - Desktop: Chrome, Edge, Opera
 *   - Mobile: Android Chrome, Android Opera, Samsung Internet
 *   - 应用内浏览器 / WebView 不展示安装入口
 */
import { BrowserDetector } from "./browser-detect";
import { PlatformDetector } from "./platform-detect";

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

type PwaInstallListener = () => void;

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

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

  /**
   * 当前环境是否支持 Web 原生 PWA 安装提示。
   * 注意：这里判断的是平台 / 浏览器 / IAB 能力，不代表当前已经拿到 prompt 事件。
   */
  isSupported(): boolean {
    if (typeof window === "undefined") return false;
    if (typeof navigator === "undefined") return false;
    if (!("serviceWorker" in navigator)) return false;
    if (BrowserDetector.isInAppBrowser()) return false;
    return this.isBrowserSupported();
  }

  /**
   * 当前浏览器是否位于原生安装提示白名单。
   *
   * 对齐 Flutter 侧逻辑：
   * - Desktop: Chrome, Edge, Opera
   * - Mobile: Android Chrome, Android Opera, Samsung Internet
   */
  isBrowserSupported(): boolean {
    if (typeof window === "undefined") return false;
    const platform = PlatformDetector.getPlatformInfo();

    if (platform.isDesktop) {
      return (
        BrowserDetector.isChrome() ||
        BrowserDetector.isEdge() ||
        BrowserDetector.isOpera()
      );
    }

    if (platform.isMobile && platform.isAndroid) {
      return (
        BrowserDetector.isChrome() ||
        BrowserDetector.isOpera() ||
        BrowserDetector.isSamsungInternet()
      );
    }

    return false;
  }

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

  /** UI 是否应该展示“安装应用”入口。 */
  canShowInstallEntry(): boolean {
    return this.isSupported() && !this.isInstalled() && this.deferred !== null;
  }

  /** 监听 beforeinstallprompt 捕获 / 安装状态变化，用于 UI 刷新安装入口展示状态。 */
  subscribe(listener: PwaInstallListener): () => void {
    this.listeners.add(listener);
    return () => {
      this.listeners.delete(listener);
    };
  }

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

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

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

    const deferred = this.deferred;
    if (!deferred) return "unavailable";
    this.clearDeferred(); // 一次性 —— 浏览器规范：同一事件只能 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();

      if (!this.isSupported() || this.isInstalled()) {
        this.clearDeferred();
        return;
      }
      this.deferred = e as BeforeInstallPromptEvent;
      this.notifyListeners();
    };
    window.addEventListener("beforeinstallprompt", handler);
    this.listenersAttached = true;
  }

  private clearDeferred(): void {
    if (this.deferred === null) return;
    this.deferred = null;
    this.notifyListeners();
  }

  private notifyListeners(): void {
    this.listeners.forEach((listener) => listener());
  }

}

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