import { describe, it, expect, vi, beforeEach } from "vitest";
import { fetchHardwareContext, formatHardwarePrompt } from "@/lib/token/agents/hardware-advisor";

describe("Hardware Advisor", () => {
  beforeEach(() => {
    vi.restoreAllMocks();
  });

  describe("fetchHardwareContext", () => {
    it("should return null when API is unavailable", async () => {
      global.fetch = vi.fn().mockRejectedValue(new Error("Network error"));
      const result = await fetchHardwareContext("http://localhost:3000", "token");
      expect(result).toBeNull();
    });

    it("should return null on non-200 response", async () => {
      global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 401 });
      const result = await fetchHardwareContext("http://localhost:3000", "token");
      expect(result).toBeNull();
    });

    it("should parse device data correctly", async () => {
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => ({
          devices: [
            {
              id: "d1",
              name: "Alienware",
              gpuName: "RTX 4090",
              vramMb: 24576,
              vramUsedMb: 8192,
              status: "online",
              loadedModels: ["qwen3:14b"],
            },
            {
              id: "d2",
              name: "Mac Mini",
              gpuName: "M4 Pro",
              vramMb: 24576,
              vramUsedMb: 0,
              status: "offline",
              loadedModels: [],
            },
          ],
        }),
      });

      const result = await fetchHardwareContext("http://localhost:3000", "token");
      expect(result).not.toBeNull();
      expect(result!.gpuCount).toBe(1); // Only online devices
      expect(result!.totalVramMb).toBe(24576);
      expect(result!.availableVramMb).toBe(16384);
      expect(result!.devices).toHaveLength(2);
    });

    it("should generate recommendations for available VRAM", async () => {
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => ({
          devices: [
            {
              id: "d1",
              name: "GPU Box",
              gpuName: "RTX 4090",
              vramMb: 24576,
              vramUsedMb: 4096,
              status: "online",
              loadedModels: ["qwen3:8b"],
            },
          ],
        }),
      });

      const result = await fetchHardwareContext("http://localhost:3000", "token");
      expect(result!.recommendations.length).toBeGreaterThan(0);
    });

    it("should recommend larger models when VRAM allows", async () => {
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => ({
          devices: [
            {
              id: "d1",
              name: "Beast",
              gpuName: "A100",
              vramMb: 81920,
              vramUsedMb: 0,
              status: "online",
              loadedModels: [],
            },
          ],
        }),
      });

      const result = await fetchHardwareContext("http://localhost:3000", "token");
      const hasLargeModelRec = result!.recommendations.some(
        (r) => r.includes("70b") || r.includes("llama3.3"),
      );
      expect(hasLargeModelRec).toBe(true);
    });

    it("should flag underutilized GPUs", async () => {
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => ({
          devices: [
            {
              id: "d1",
              name: "Idle Box",
              gpuName: "RTX 4090",
              vramMb: 24576,
              vramUsedMb: 2048,
              status: "online",
              loadedModels: [],
            },
          ],
        }),
      });

      const result = await fetchHardwareContext("http://localhost:3000", "token");
      const hasUtilRec = result!.recommendations.some((r) => r.includes("utilized"));
      expect(hasUtilRec).toBe(true);
    });

    it("should handle empty devices array", async () => {
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => ({ devices: [] }),
      });

      const result = await fetchHardwareContext("http://localhost:3000", "token");
      expect(result).not.toBeNull();
      expect(result!.gpuCount).toBe(0);
      expect(result!.totalVramMb).toBe(0);
      expect(result!.recommendations).toContain(
        "No online devices. Connect a device with `llmhosts up` to get started.",
      );
    });

    it("should handle missing fields in device data", async () => {
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => ({
          devices: [{ id: "d1", status: "online" }],
        }),
      });

      const result = await fetchHardwareContext("http://localhost:3000", "token");
      expect(result).not.toBeNull();
      expect(result!.devices[0].name).toBe("Unknown");
      expect(result!.devices[0].gpuName).toBe("Unknown GPU");
      expect(result!.devices[0].vramMb).toBe(0);
    });

    it("should recommend qwen3:8b for 8GB VRAM", async () => {
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => ({
          devices: [
            {
              id: "d1",
              name: "RTX Box",
              gpuName: "RTX 4060",
              vramMb: 8192,
              vramUsedMb: 0,
              status: "online",
              loadedModels: [],
            },
          ],
        }),
      });

      const result = await fetchHardwareContext("http://localhost:3000", "token");
      const has8bRec = result!.recommendations.some((r) => r.includes("qwen3:8b"));
      expect(has8bRec).toBe(true);
    });
  });

  describe("formatHardwarePrompt", () => {
    it("should format context with VRAM info", () => {
      const ctx = {
        totalVramMb: 24576,
        availableVramMb: 16384,
        gpuCount: 1,
        devices: [
          {
            deviceId: "d1",
            name: "Alienware",
            gpuName: "RTX 4090",
            vramMb: 24576,
            vramUsedMb: 8192,
            status: "online" as const,
            loadedModels: ["qwen3:14b"],
          },
        ],
        recommendations: ["qwen3:32b fits your available VRAM."],
      };

      const prompt = formatHardwarePrompt(ctx);
      expect(prompt).toContain("Hardware Context");
      expect(prompt).toContain("24.0 GB");
      expect(prompt).toContain("RTX 4090");
      expect(prompt).toContain("qwen3:14b");
    });

    it("should show recommendations section", () => {
      const ctx = {
        totalVramMb: 8192,
        availableVramMb: 8192,
        gpuCount: 1,
        devices: [],
        recommendations: ["Try qwen3:8b for your hardware."],
      };

      const prompt = formatHardwarePrompt(ctx);
      expect(prompt).toContain("Recommendations");
      expect(prompt).toContain("qwen3:8b");
    });

    it("should show device status labels", () => {
      const ctx = {
        totalVramMb: 0,
        availableVramMb: 0,
        gpuCount: 0,
        devices: [
          {
            deviceId: "d1",
            name: "Online Box",
            gpuName: "RTX 4090",
            vramMb: 24576,
            vramUsedMb: 0,
            status: "online" as const,
            loadedModels: [],
          },
          {
            deviceId: "d2",
            name: "Degraded Box",
            gpuName: "RTX 3090",
            vramMb: 24576,
            vramUsedMb: 0,
            status: "degraded" as const,
            loadedModels: [],
          },
          {
            deviceId: "d3",
            name: "Offline Box",
            gpuName: "RTX 3060",
            vramMb: 12288,
            vramUsedMb: 0,
            status: "offline" as const,
            loadedModels: [],
          },
        ],
        recommendations: [],
      };

      const prompt = formatHardwarePrompt(ctx);
      expect(prompt).toContain("[ONLINE]");
      expect(prompt).toContain("[DEGRADED]");
      expect(prompt).toContain("[OFFLINE]");
    });

    it("should omit recommendations section when empty", () => {
      const ctx = {
        totalVramMb: 8192,
        availableVramMb: 8192,
        gpuCount: 1,
        devices: [],
        recommendations: [],
      };

      const prompt = formatHardwarePrompt(ctx);
      expect(prompt).not.toContain("Recommendations");
    });
  });
});
