import type { ChatCompletionMessageParam } from "openai/resources/chat/completions";
import { describe, it, expect, beforeAll, afterAll } from "vitest";

import { startServer, stopServer, getTestClient } from "./setup";

describe("Chat Completions", () => {
  beforeAll(async () => {
    await startServer();
  });

  afterAll(async () => {
    await stopServer();
  });

  describe("Non-streaming", () => {
    it("should return a valid chat completion", async () => {
      const client = getTestClient();

      const response = await client.chat.completions.create({
        model: "gpt-4o",
        messages: [{ role: "user", content: "Hello!" }],
      });

      expect(response.id).toMatch(/^chatcmpl-/);
      expect(response.object).toBe("chat.completion");
      expect(response.model).toBe("gpt-4o");
      expect(response.choices).toHaveLength(1);
      expect(response.choices[0].message.role).toBe("assistant");
      expect(response.choices[0].message.content).toBeTruthy();
      expect(response.choices[0].finish_reason).toBe("stop");
      expect(response.usage).toBeDefined();
      expect(response.usage?.prompt_tokens).toBeGreaterThan(0);
      expect(response.usage?.completion_tokens).toBeGreaterThan(0);
      expect(response.usage?.total_tokens).toBe(
        (response.usage?.prompt_tokens ?? 0) +
          (response.usage?.completion_tokens ?? 0)
      );
    });

    it("should handle multiple messages", async () => {
      const client = getTestClient();

      const messages: ChatCompletionMessageParam[] = [
        { role: "system", content: "You are a helpful assistant." },
        { role: "user", content: "Hello!" },
        { role: "assistant", content: "Hi there!" },
        { role: "user", content: "How are you?" },
      ];

      const response = await client.chat.completions.create({
        model: "gpt-4o",
        messages,
      });

      expect(response.choices[0].message.content).toBeTruthy();
    });

    it("should reject requests without model", async () => {
      const client = getTestClient();

      await expect(
        client.chat.completions.create({
          model: "",
          messages: [{ role: "user", content: "Hello!" }],
        })
      ).rejects.toThrow();
    });

    it("should reject requests without messages", async () => {
      const client = getTestClient();

      await expect(
        client.chat.completions.create({
          model: "gpt-4o",
          messages: [] as ChatCompletionMessageParam[],
        })
      ).rejects.toThrow();
    });
  });

  describe("Streaming", () => {
    it("should stream a valid chat completion", async () => {
      const client = getTestClient();

      const stream = await client.chat.completions.create({
        model: "gpt-4o",
        messages: [{ role: "user", content: "Hello!" }],
        stream: true,
      });

      const chunks: string[] = [];
      let hasRole = false;
      let hasFinishReason = false;
      let completionId: string | null = null;

      for await (const chunk of stream) {
        if (completionId === null) {
          completionId = chunk.id;
        } else {
          // All chunks should have the same ID
          expect(chunk.id).toBe(completionId);
        }

        expect(chunk.object).toBe("chat.completion.chunk");
        expect(chunk.model).toBe("gpt-4o");

        if (chunk.choices[0]?.delta.role === "assistant") {
          hasRole = true;
        }

        if (chunk.choices[0]?.delta.content) {
          chunks.push(chunk.choices[0].delta.content);
        }

        if (chunk.choices[0]?.finish_reason === "stop") {
          hasFinishReason = true;
        }
      }

      expect(hasRole).toBe(true);
      expect(hasFinishReason).toBe(true);
      expect(chunks.length).toBeGreaterThan(0);

      const fullContent = chunks.join("");
      expect(fullContent).toBeTruthy();
      expect(fullContent.length).toBeGreaterThan(10);
    });

    it("should include usage when stream_options.include_usage is true", async () => {
      const client = getTestClient();

      const stream = await client.chat.completions.create({
        model: "gpt-4o",
        messages: [{ role: "user", content: "Hello!" }],
        stream: true,
        stream_options: { include_usage: true },
      });

      let hasUsage = false;

      for await (const chunk of stream) {
        if (chunk.usage) {
          hasUsage = true;
          expect(chunk.usage.prompt_tokens).toBeGreaterThan(0);
          expect(chunk.usage.completion_tokens).toBeGreaterThan(0);
          expect(chunk.usage.total_tokens).toBe(
            chunk.usage.prompt_tokens + chunk.usage.completion_tokens
          );
        }
      }

      expect(hasUsage).toBe(true);
    });
  });
});
