"use client";

/**
 * 设备 ID 工具类
 *
 * 生产环境使用 fingerprintjs 生成客户端设备指纹。
 * 非生产环境使用本地 UUID，避免开发 / 预发布调试依赖真实设备指纹。
 * 
 *
 * 注：仅在浏览器环境可用；SSR/Server Components 中调用会抛错。
 */
import { AuthStorage } from "../data/storage/auth/auth_storage";
import { AppEnvUtil } from "./app-env";

export class DeviceIdentifier {
  private cachedId: string | null = null;
  private initPromise: Promise<string> | null = null;

  /**
   * 获取或生成设备 ID
   *
   * 优先级：内存缓存 → localStorage 缓存 → 非生产 UUID / 生产 fingerprintjs
   * 并发安全：多个并发调用会共享同一个初始化 Promise
   */
  async getDeviceId(): Promise<string> {
    if (this.cachedId) return this.cachedId;
    if (this.initPromise) return this.initPromise;

    this.initPromise = this.initialize();
    this.cachedId = await this.initPromise;
    return this.cachedId;
  }

  private async initialize(): Promise<string> {
    // 1. 优先从 localStorage 读取
    const storedR = await AuthStorage.getInstance().getDeviceId();
    if (storedR.success && storedR.data && storedR.data.length > 0) {
      return storedR.data;
    }

    // 2. SSR/Node 环境守卫：调用方必须保证在浏览器中调用
    if (typeof window === "undefined") {
      throw new Error(
        "DeviceIdentifier only works in browser environment. " +
          "Call getDeviceId() from a Client Component or browser code."
      );
    }

    // 3. 非生产环境不使用真实设备指纹，生成并持久化一个本地 UUID 即可。
    if (!AppEnvUtil.isProduction()) {
      const deviceId = createUuidV4();
      await AuthStorage.getInstance().setDeviceId(deviceId);
      return deviceId;
    }

    // 4. 生产环境动态加载 fingerprintjs（避免 Next.js SSR 打包分析时执行）
    const { load } = await import("@fingerprintjs/fingerprintjs");
    const fp = await load();
    const result = await fp.get();

    // 5. 持久化
    await AuthStorage.getInstance().setDeviceId(result.visitorId);
    return result.visitorId;
  }
}

function createUuidV4(): string {
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
    return crypto.randomUUID();
  }

  const bytes = new Uint8Array(16);
  if (typeof crypto !== "undefined" && crypto.getRandomValues) {
    crypto.getRandomValues(bytes);
  } else {
    for (let i = 0; i < bytes.length; i += 1) {
      bytes[i] = Math.floor(Math.random() * 256);
    }
  }

  bytes[6] = (bytes[6] & 0x0f) | 0x40;
  bytes[8] = (bytes[8] & 0x3f) | 0x80;

  const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
  return [
    hex.slice(0, 4).join(""),
    hex.slice(4, 6).join(""),
    hex.slice(6, 8).join(""),
    hex.slice(8, 10).join(""),
    hex.slice(10, 16).join(""),
  ].join("-");
}

/**
 * 全局单例
 */
export const deviceIdentifier = new DeviceIdentifier();
