/**
 * Hardware Advisor agent for Token.
 *
 * Enriches Token's context with real GPU/VRAM data from connected devices
 * so recommendations are grounded in actual hardware capabilities.
 */

interface DeviceHardware {
  deviceId: string;
  name: string;
  gpuName: string;
  vramMb: number;
  vramUsedMb: number;
  status: "online" | "offline" | "degraded";
  loadedModels: string[];
}

export interface HardwareContext {
  totalVramMb: number;
  availableVramMb: number;
  gpuCount: number;
  devices: DeviceHardware[];
  recommendations: string[];
}

/**
 * Fetch device hardware info from the internal API.
 * Returns null if the API is unavailable (e.g., no devices registered).
 */
export async function fetchHardwareContext(
  baseUrl: string,
  authToken: string,
): Promise<HardwareContext | null> {
  try {
    const response = await fetch(`${baseUrl}/api/devices`, {
      headers: { Authorization: `Bearer ${authToken}` },
    });

    if (!response.ok) return null;

    const data = await response.json();
    const devices: DeviceHardware[] = (data.devices || []).map((d: Record<string, unknown>) => ({
      deviceId: String(d.id || ""),
      name: String(d.name || "Unknown"),
      gpuName: String(d.gpuName || "Unknown GPU"),
      vramMb: Number(d.vramMb || 0),
      vramUsedMb: Number(d.vramUsedMb || 0),
      status: d.status === "online" ? "online" : d.status === "degraded" ? "degraded" : "offline",
      loadedModels: Array.isArray(d.loadedModels) ? d.loadedModels.map(String) : [],
    }));

    const onlineDevices = devices.filter((d) => d.status === "online");
    const totalVram = onlineDevices.reduce((sum, d) => sum + d.vramMb, 0);
    const usedVram = onlineDevices.reduce((sum, d) => sum + d.vramUsedMb, 0);

    return {
      totalVramMb: totalVram,
      availableVramMb: totalVram - usedVram,
      gpuCount: onlineDevices.length,
      devices,
      recommendations: generateRecommendations(onlineDevices, totalVram - usedVram),
    };
  } catch {
    return null;
  }
}

/**
 * Generate VRAM-aware model recommendations based on available hardware.
 */
function generateRecommendations(devices: DeviceHardware[], availableVramMb: number): string[] {
  const recs: string[] = [];

  if (devices.length === 0) {
    recs.push("No online devices. Connect a device with `llmhosts up` to get started.");
    return recs;
  }

  const availableGb = availableVramMb / 1024;

  if (availableGb >= 40) {
    recs.push("You have enough VRAM for llama3.3:70b (40 GB) — the most capable open model.");
  } else if (availableGb >= 22) {
    recs.push("You can run qwen3:32b (20 GB) — excellent for complex reasoning and code.");
  } else if (availableGb >= 10) {
    recs.push("qwen3:14b fits your VRAM well — great balance of capability and speed.");
  } else if (availableGb >= 7) {
    recs.push("qwen3:8b is your best fit — solid performance on 8 GB VRAM.");
  } else if (availableGb >= 4) {
    recs.push("qwen3:4b works on your hardware — good for lightweight tasks.");
  } else if (availableGb > 0) {
    recs.push("Limited VRAM available. Consider qwen3:1.7b for basic tasks.");
  }

  // Check for underutilized GPUs
  for (const device of devices) {
    const utilization = device.vramUsedMb / device.vramMb;
    if (utilization < 0.3 && device.vramMb > 4096) {
      recs.push(
        `${device.name} is only ${Math.round(utilization * 100)}% utilized — consider loading an additional model.`,
      );
    }
  }

  return recs;
}

/**
 * Format hardware context as a system prompt section.
 */
export function formatHardwarePrompt(ctx: HardwareContext): string {
  const lines = ["## Hardware Context (Live)"];
  lines.push(`- Total VRAM: ${(ctx.totalVramMb / 1024).toFixed(1)} GB across ${ctx.gpuCount} GPU(s)`);
  lines.push(`- Available VRAM: ${(ctx.availableVramMb / 1024).toFixed(1)} GB`);

  for (const device of ctx.devices) {
    const status =
      device.status === "online" ? "ONLINE" : device.status === "degraded" ? "DEGRADED" : "OFFLINE";
    lines.push(
      `- ${device.name}: ${device.gpuName} (${(device.vramMb / 1024).toFixed(1)} GB) [${status}]`,
    );
    if (device.loadedModels.length > 0) {
      lines.push(`  Models: ${device.loadedModels.join(", ")}`);
    }
  }

  if (ctx.recommendations.length > 0) {
    lines.push("\n### Recommendations");
    for (const rec of ctx.recommendations) {
      lines.push(`- ${rec}`);
    }
  }

  return lines.join("\n");
}
