import os from "node:os";
import si from "systeminformation";

export type HardwareTier = "gpu" | "apple_silicon" | "cpu_only";

export interface HardwareInfo {
  totalGb: number;
  freeGb: number;
  usableGb: number;
  platform: NodeJS.Platform;
  gpuName: string | null;
  gpuVramGb: number | null;
  unifiedMemory: boolean;
  hardwareTier: HardwareTier;
  cpuModel: string | null;
}

const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes

let cache: HardwareInfo | null = null;
let cacheExpiry = 0;

/** Reset the hardware detection cache. Exposed for testing only. */
export function _resetHardwareCache(): void {
  cache = null;
  cacheExpiry = 0;
}

export function hardwareService() {
  async function detect(): Promise<HardwareInfo> {
    const now = Date.now();
    if (cache && now < cacheExpiry) {
      return cache;
    }

    const totalBytes = os.totalmem();
    const freeBytes = os.freemem();
    const totalGb = Math.round((totalBytes / (1024 * 1024 * 1024)) * 10) / 10;
    const freeGb = Math.round((freeBytes / (1024 * 1024 * 1024)) * 10) / 10;
    const usableGb = Math.round((freeBytes * 0.75) / (1024 * 1024 * 1024) * 10) / 10;
    const cpuModel = os.cpus()[0]?.model ?? null;
    const platform = process.platform;

    // Apple Silicon detection: darwin platform + CPU brand starting with "Apple"
    if (platform === "darwin" && cpuModel?.startsWith("Apple")) {
      const info: HardwareInfo = {
        totalGb,
        freeGb,
        usableGb,
        platform,
        gpuName: null,
        gpuVramGb: null,
        unifiedMemory: true,
        hardwareTier: "apple_silicon",
        cpuModel,
      };
      cache = info;
      cacheExpiry = now + CACHE_TTL_MS;
      return info;
    }

    // Non-Apple Silicon: probe GPU with 3-second timeout
    let gpuName: string | null = null;
    let gpuVramGb: number | null = null;
    let hardwareTier: HardwareTier = "cpu_only";

    try {
      const timeoutPromise = new Promise<never>((_resolve, reject) => {
        setTimeout(() => reject(new Error("GPU detection timeout")), 3000);
      });
      const graphicsResult = await Promise.race([si.graphics(), timeoutPromise]);
      const controllers = graphicsResult.controllers ?? [];
      if (controllers.length > 0 && controllers[0]) {
        const vramMb = controllers[0].vram ?? 0;
        const vramGb = vramMb / 1024;
        if (vramGb >= 4) {
          gpuName = controllers[0].model ?? null;
          gpuVramGb = Math.round(vramGb * 10) / 10;
          hardwareTier = "gpu";
        }
      }
    } catch {
      // Timeout or error — degrade to cpu_only
      hardwareTier = "cpu_only";
    }

    const info: HardwareInfo = {
      totalGb,
      freeGb,
      usableGb,
      platform,
      gpuName,
      gpuVramGb,
      unifiedMemory: false,
      hardwareTier,
      cpuModel,
    };

    cache = info;
    cacheExpiry = now + CACHE_TTL_MS;
    return info;
  }

  return { detect };
}
