import { describe, it, expect, beforeEach, vi } from "vitest";

vi.mock("@/lib/db", () => ({
  db: {
    device: {
      findMany: vi.fn(),
    },
  },
}));

vi.mock("@/lib/auth", () => ({
  auth: vi.fn(),
}));

vi.mock("@/lib/logger", () => ({
  logger: { info: vi.fn(), error: vi.fn(), warn: vi.fn() },
}));

import { auth } from "@/lib/auth";
import { db } from "@/lib/db";

describe("POST /api/marketplace/route", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("returns 401 when not authenticated", async () => {
    vi.mocked(auth).mockResolvedValue(null as never);

    const { POST } = await import("@/app/api/marketplace/route/route");
    const response = await POST(
      new Request("http://localhost/api/marketplace/route", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ model: "llama3.2:3b" }),
      }),
    );

    expect(response.status).toBe(401);
    const data = await response.json();
    expect(data.error).toBe("Unauthorized");
  });

  it("returns 400 when model is missing", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.device.findMany).mockResolvedValue([]);

    const { POST } = await import("@/app/api/marketplace/route/route");
    const response = await POST(
      new Request("http://localhost/api/marketplace/route", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({}),
      }),
    );

    expect(response.status).toBe(400);
    const data = await response.json();
    expect(data.error).toBe("model is required");
  });

  it("returns 404 when no nodes available for model", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.device.findMany).mockResolvedValue([
      {
        id: "dev-1",
        userId: "user-2",
        name: "My GPU",
        status: "online",
        tunnelUrl: "https://tunnel.example.com",
        models: JSON.stringify(["qwen3:8b"]),
        marketplaceEnabled: true,
        marketplacePriceCents: 10,
        latencyMs: 100,
        lastSeen: new Date(),
      },
    ] as never[]);

    const { POST } = await import("@/app/api/marketplace/route/route");
    const response = await POST(
      new Request("http://localhost/api/marketplace/route", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ model: "llama3.2:3b" }),
      }),
    );

    expect(response.status).toBe(404);
    const data = await response.json();
    expect(data.error).toContain("No available nodes");
    expect(data.model).toBe("llama3.2:3b");
  });

  it("returns 200 with selected node when a device has the model", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.device.findMany).mockResolvedValue([
      {
        id: "dev-1",
        userId: "user-2",
        name: "My GPU",
        status: "online",
        tunnelUrl: "https://tunnel.example.com",
        models: JSON.stringify(["llama3.2:3b", "qwen3:8b"]),
        marketplaceEnabled: true,
        marketplacePriceCents: 10,
        latencyMs: 80,
        lastSeen: new Date(),
      },
    ] as never[]);

    const { POST } = await import("@/app/api/marketplace/route/route");
    const response = await POST(
      new Request("http://localhost/api/marketplace/route", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ model: "llama3.2:3b" }),
      }),
    );

    expect(response.status).toBe(200);
    const data = await response.json();
    expect(data.deviceId).toBe("dev-1");
    expect(data.operatorId).toBe("user-2");
    expect(data.tunnelUrl).toBe("https://tunnel.example.com");
    expect(data.model).toBe("llama3.2:3b");
    expect(typeof data.estimatedCostPerMToken).toBe("number");
    expect(data.reason).toBeTruthy();
  });
});
