import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import os from "node:os";

// We mock systeminformation before importing hardware module
vi.mock("systeminformation", () => ({
  default: {
    graphics: vi.fn(),
  },
}));

vi.mock("node:os", () => ({
  default: {
    cpus: vi.fn(),
    totalmem: vi.fn(),
  },
}));

// Import after mocks
const { detectHardware } = await import("../hardware.js");

const mockOs = vi.mocked(os);

describe("detectHardware", () => {
  beforeEach(() => {
    vi.resetAllMocks();
    // Default: 16GB RAM
    mockOs.totalmem.mockReturnValue(16 * 1024 * 1024 * 1024);
  });

  afterEach(() => {
    vi.unstubAllEnvs();
  });

  it("detects apple_silicon on darwin with Apple CPU", async () => {
    vi.stubEnv("FORCE_PLATFORM", "darwin");
    mockOs.cpus.mockReturnValue([
      { model: "Apple M4 Pro", speed: 3200, times: { user: 0, nice: 0, sys: 0, idle: 0, irq: 0 } },
    ] as any);

    // Override platform check — use internal platform stub
    const result = await detectHardware("darwin");
    expect(result.tier).toBe("apple_silicon");
    expect(result.totalGb).toBeGreaterThan(0);
  });

  it("detects gpu on linux with NVIDIA GPU with sufficient VRAM", async () => {
    const si = await import("systeminformation");
    vi.mocked(si.default.graphics).mockResolvedValue({
      controllers: [
        {
          model: "NVIDIA GeForce RTX 4090",
          vram: 24576, // 24GB in MB
          vramDynamic: false,
        },
      ],
      displays: [],
    } as any);
    mockOs.cpus.mockReturnValue([
      { model: "Intel Core i9-13900K", speed: 3200, times: { user: 0, nice: 0, sys: 0, idle: 0, irq: 0 } },
    ] as any);

    const result = await detectHardware("linux");
    expect(result.tier).toBe("gpu");
    expect(result.gpuName).toBe("NVIDIA GeForce RTX 4090");
    expect(result.gpuVramGb).toBe(24);
    expect(result.totalGb).toBeGreaterThan(0);
  });

  it("returns cpu_only when si.graphics times out (3 second timeout)", async () => {
    const si = await import("systeminformation");
    vi.mocked(si.default.graphics).mockImplementation(
      () =>
        new Promise((resolve) => {
          // Never resolves — simulates a very long hang
          setTimeout(resolve, 60_000);
        })
    );
    mockOs.cpus.mockReturnValue([
      { model: "Intel Core i5", speed: 2400, times: { user: 0, nice: 0, sys: 0, idle: 0, irq: 0 } },
    ] as any);

    // Use fake timers so we don't actually wait 3 seconds
    vi.useFakeTimers();
    const resultPromise = detectHardware("linux");
    vi.advanceTimersByTime(3001);
    const result = await resultPromise;
    vi.useRealTimers();

    expect(result.tier).toBe("cpu_only");
  });

  it("returns cpu_only when GPU VRAM is below 4 GB", async () => {
    const si = await import("systeminformation");
    vi.mocked(si.default.graphics).mockResolvedValue({
      controllers: [
        {
          model: "Intel UHD Graphics 630",
          vram: 2048, // 2GB — below threshold
          vramDynamic: false,
        },
      ],
      displays: [],
    } as any);
    mockOs.cpus.mockReturnValue([
      { model: "Intel Core i7", speed: 2800, times: { user: 0, nice: 0, sys: 0, idle: 0, irq: 0 } },
    ] as any);

    const result = await detectHardware("linux");
    expect(result.tier).toBe("cpu_only");
  });

  it("returns cpu_only when si.graphics throws an error", async () => {
    const si = await import("systeminformation");
    vi.mocked(si.default.graphics).mockRejectedValue(new Error("graphics query failed"));
    mockOs.cpus.mockReturnValue([
      { model: "AMD Ryzen 9", speed: 3600, times: { user: 0, nice: 0, sys: 0, idle: 0, irq: 0 } },
    ] as any);

    const result = await detectHardware("linux");
    expect(result.tier).toBe("cpu_only");
  });

  it("returns cpu_only when controllers array is empty", async () => {
    const si = await import("systeminformation");
    vi.mocked(si.default.graphics).mockResolvedValue({
      controllers: [],
      displays: [],
    } as any);
    mockOs.cpus.mockReturnValue([
      { model: "AMD Ryzen 5", speed: 3200, times: { user: 0, nice: 0, sys: 0, idle: 0, irq: 0 } },
    ] as any);

    const result = await detectHardware("linux");
    expect(result.tier).toBe("cpu_only");
  });
});
