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

vi.mock("@/lib/db", () => ({
  db: {
    operatorSettings: {
      findUnique: vi.fn(),
      upsert: vi.fn(),
    },
    user: {
      findUnique: 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("GET /api/dashboard/operator/settings", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

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

    const { GET } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const response = await GET();

    expect(response.status).toBe(401);
  });

  it("returns defaults when no settings exist", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.operatorSettings.findUnique).mockResolvedValue(null);

    const { GET } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const response = await GET();
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.marketplaceEnabled).toBe(false);
    expect(data.pricePerMToken).toBe(0.1);
    expect(data.autoPrice).toBe(true);
    expect(data.availableModels).toEqual([]);
  });

  it("returns existing settings with parsed models", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.operatorSettings.findUnique).mockResolvedValue({
      id: "os-1",
      userId: "user-1",
      marketplaceEnabled: true,
      pricePerMToken: 0.15,
      autoPrice: true,
      availableModels: '["qwen3:14b","llama3.3:70b"]',
      minPrice: 0.01,
      maxPrice: 1.0,
      createdAt: new Date(),
      updatedAt: new Date(),
    } as never);

    const { GET } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const response = await GET();
    const data = await response.json();

    expect(data.marketplaceEnabled).toBe(true);
    expect(data.pricePerMToken).toBe(0.15);
    expect(data.availableModels).toEqual(["qwen3:14b", "llama3.3:70b"]);
  });

  it("returns 500 on db error", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.operatorSettings.findUnique).mockRejectedValue(
      new Error("DB down"),
    );

    const { GET } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const response = await GET();

    expect(response.status).toBe(500);
  });
});

describe("PUT /api/dashboard/operator/settings", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

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

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ marketplaceEnabled: true }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(401);
  });

  it("upserts settings with marketplace toggle when terms accepted", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.user.findUnique).mockResolvedValue({
      marketplaceTermsAcceptedAt: new Date("2026-02-20"),
    } as never);
    vi.mocked(db.operatorSettings.upsert).mockResolvedValue({
      id: "os-1",
      userId: "user-1",
      marketplaceEnabled: true,
      pricePerMToken: 0.1,
      autoPrice: true,
      availableModels: "[]",
      minPrice: 0.01,
      maxPrice: 1.0,
      createdAt: new Date(),
      updatedAt: new Date(),
    } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ marketplaceEnabled: true }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(200);
    expect(db.operatorSettings.upsert).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { userId: "user-1" },
        create: expect.objectContaining({ marketplaceEnabled: true }),
        update: expect.objectContaining({ marketplaceEnabled: true }),
      }),
    );
  });

  it("upserts settings with price change", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.operatorSettings.upsert).mockResolvedValue({
      id: "os-1",
      userId: "user-1",
      marketplaceEnabled: false,
      pricePerMToken: 0.25,
      autoPrice: true,
      availableModels: "[]",
      minPrice: 0.01,
      maxPrice: 1.0,
      createdAt: new Date(),
      updatedAt: new Date(),
    } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ pricePerMToken: 0.25 }),
      },
    );
    const response = await PUT(request);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.pricePerMToken).toBe(0.25);
  });

  it("rejects price below minimum ($0.01)", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ pricePerMToken: 0.005 }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(400);
    const data = await response.json();
    expect(data.error).toContain("$0.01");
    expect(db.operatorSettings.upsert).not.toHaveBeenCalled();
  });

  it("rejects price above maximum ($1.00)", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ pricePerMToken: 1.50 }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(400);
    const data = await response.json();
    expect(data.error).toContain("$1.00");
    expect(db.operatorSettings.upsert).not.toHaveBeenCalled();
  });

  it("rejects non-numeric price", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ pricePerMToken: "not-a-number" }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(400);
    expect(db.operatorSettings.upsert).not.toHaveBeenCalled();
  });

  it("accepts boundary price $0.01 (minimum)", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.operatorSettings.upsert).mockResolvedValue({
      id: "os-1",
      userId: "user-1",
      marketplaceEnabled: false,
      pricePerMToken: 0.01,
      autoPrice: true,
      availableModels: "[]",
      minPrice: 0.01,
      maxPrice: 1.0,
      createdAt: new Date(),
      updatedAt: new Date(),
    } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ pricePerMToken: 0.01 }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(200);
  });

  it("accepts boundary price $1.00 (maximum)", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.operatorSettings.upsert).mockResolvedValue({
      id: "os-1",
      userId: "user-1",
      marketplaceEnabled: false,
      pricePerMToken: 1.0,
      autoPrice: true,
      availableModels: "[]",
      minPrice: 0.01,
      maxPrice: 1.0,
      createdAt: new Date(),
      updatedAt: new Date(),
    } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ pricePerMToken: 1.0 }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(200);
  });

  it("upserts with available models as JSON string", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.operatorSettings.upsert).mockResolvedValue({
      id: "os-1",
      userId: "user-1",
      marketplaceEnabled: true,
      pricePerMToken: 0.1,
      autoPrice: true,
      availableModels: '["qwen3:14b"]',
      minPrice: 0.01,
      maxPrice: 1.0,
      createdAt: new Date(),
      updatedAt: new Date(),
    } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ availableModels: ["qwen3:14b"] }),
      },
    );
    const response = await PUT(request);
    const data = await response.json();

    expect(data.availableModels).toEqual(["qwen3:14b"]);
    expect(db.operatorSettings.upsert).toHaveBeenCalledWith(
      expect.objectContaining({
        update: expect.objectContaining({
          availableModels: '["qwen3:14b"]',
        }),
      }),
    );
  });

  it("returns 500 on db error", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.user.findUnique).mockResolvedValue({
      marketplaceTermsAcceptedAt: new Date("2026-02-20"),
    } as never);
    vi.mocked(db.operatorSettings.upsert).mockRejectedValue(
      new Error("DB down"),
    );

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ marketplaceEnabled: true }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(500);
  });

  it("returns 403 when enabling marketplace without accepting terms", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.user.findUnique).mockResolvedValue({
      marketplaceTermsAcceptedAt: null,
    } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ marketplaceEnabled: true }),
      },
    );
    const response = await PUT(request);
    const data = await response.json();

    expect(response.status).toBe(403);
    expect(data.error).toBe(
      "You must accept marketplace terms before enabling.",
    );
    expect(db.operatorSettings.upsert).not.toHaveBeenCalled();
  });

  it("returns 403 when user not found during terms check", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.user.findUnique).mockResolvedValue(null);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ marketplaceEnabled: true }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(403);
    expect(db.operatorSettings.upsert).not.toHaveBeenCalled();
  });

  it("skips terms check when marketplaceEnabled is false", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.operatorSettings.upsert).mockResolvedValue({
      id: "os-1",
      userId: "user-1",
      marketplaceEnabled: false,
      pricePerMToken: 0.1,
      autoPrice: true,
      availableModels: "[]",
      minPrice: 0.01,
      maxPrice: 1.0,
      createdAt: new Date(),
      updatedAt: new Date(),
    } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ marketplaceEnabled: false }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(200);
    // user.findUnique should NOT be called since marketplaceEnabled is false
    expect(db.user.findUnique).not.toHaveBeenCalled();
  });

  it("skips terms check when only updating price (not enabling)", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "user-1" } } as never);
    vi.mocked(db.operatorSettings.upsert).mockResolvedValue({
      id: "os-1",
      userId: "user-1",
      marketplaceEnabled: false,
      pricePerMToken: 0.5,
      autoPrice: true,
      availableModels: "[]",
      minPrice: 0.01,
      maxPrice: 1.0,
      createdAt: new Date(),
      updatedAt: new Date(),
    } as never);

    const { PUT } = await import(
      "@/app/api/dashboard/operator/settings/route"
    );
    const request = new Request(
      "http://localhost/api/dashboard/operator/settings",
      {
        method: "PUT",
        body: JSON.stringify({ pricePerMToken: 0.5 }),
      },
    );
    const response = await PUT(request);

    expect(response.status).toBe(200);
    // Terms check should not be triggered when not enabling marketplace
    expect(db.user.findUnique).not.toHaveBeenCalled();
  });
});
