import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { mkdtemp, rm, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { readTranscript, appendTranscript, transcriptPath, TRANSCRIPT_DIR } from "../../src/agent/transcript.js";
import type { ModelMessage } from "ai";

const sample: ModelMessage = { role: "user", content: "hello" };
const reply: ModelMessage = { role: "assistant", content: "hi" };

describe("transcript JSONL", () => {
  let dir: string;
  let path: string;

  beforeEach(async () => {
    dir = await mkdtemp(join(tmpdir(), "hub-transcript-"));
    path = join(dir, "session.jsonl");
  });
  afterEach(async () => {
    await rm(dir, { recursive: true, force: true });
  });

  it("readTranscript returns empty array for nonexistent file", async () => {
    expect(await readTranscript(path)).toEqual([]);
  });

  it("appendTranscript creates parent dirs and writes JSONL", async () => {
    await appendTranscript(path, [sample]);
    const content = await readFile(path, "utf8");
    expect(content.trim()).toBe(JSON.stringify(sample));
  });

  it("readTranscript reads back appended messages", async () => {
    await appendTranscript(path, [sample, reply]);
    const messages = await readTranscript(path);
    expect(messages).toHaveLength(2);
    expect(messages[0]).toEqual(sample);
    expect(messages[1]).toEqual(reply);
  });

  it("multiple appends accumulate", async () => {
    await appendTranscript(path, [sample]);
    await appendTranscript(path, [reply]);
    const messages = await readTranscript(path);
    expect(messages).toHaveLength(2);
  });

  it("skips corrupt lines (partial write recovery)", async () => {
    await appendTranscript(path, [sample]);
    // Append a corrupt line (simulating a crash mid-write).
    await writeFile(path, '{"broken json\n', { flag: "a" });
    const messages = await readTranscript(path);
    expect(messages).toHaveLength(1); // only the valid line
  });

  it("transcriptPath builds the expected path", () => {
    const p = transcriptPath("/ws", "sess-1");
    expect(p).toBe(join("/ws", TRANSCRIPT_DIR, "sess-1.jsonl"));
  });
});
