import assert from "node:assert/strict";
import test from "node:test";
import { AudioRepository } from "../../lib/server/repositories/audio.ts";
import { createTestD1 } from "../helpers/sqlite-d1.mjs";

function seed(database) {
  database.exec(`
    INSERT INTO books (id, title, author, format, source_object_key, source_bytes, status)
      VALUES ('book-1', '测试书', '作者', 'pdf', 'sources/book-1/book.pdf', 100, 'ready');
    INSERT INTO chapters (id, book_id, title, position) VALUES ('chapter-1', 'book-1', '第一章', 0);
    INSERT INTO segments (id, chapter_id, position, text, language, content_hash, text_version, included)
      VALUES ('segment-1', 'chapter-1', 0, '第一句。', 'zh-CN', '${"a".repeat(64)}', 2, 1);
    INSERT INTO segments (id, chapter_id, position, text, language, content_hash, text_version, included)
      VALUES ('segment-skip', 'chapter-1', 1, 'E=mc2', 'en-US', '${"b".repeat(64)}', 1, 0);
  `);
}

test("authorizes audio only for a current included stored segment", async () => {
  const { d1, database } = await createTestD1();
  seed(database);
  const repository = new AudioRepository(d1);

  assert.deepEqual(await repository.segmentForAudio("segment-1"), {
    id: "segment-1",
    bookId: "book-1",
    chapterId: "chapter-1",
    text: "第一句。",
    language: "zh-CN",
    textVersion: 2,
  });
  assert.equal(await repository.segmentForAudio("segment-skip"), null);
  assert.equal(await repository.segmentForAudio("missing"), null);
});

test("registers a versioned ready MP3 and exposes only an API URL", async () => {
  const { d1, database } = await createTestD1();
  seed(database);
  const repository = new AudioRepository(d1);
  const now = "2026-08-13T01:00:00.000Z";

  const audio = await repository.registerReady({
    segmentId: "segment-1",
    textVersion: 2,
    voice: "zh-CN-XiaoxiaoNeural",
    bytes: 1234,
    durationMs: 2800,
    objectKey: "audio/book-1/segment-1/v2/zh-CN-XiaoxiaoNeural.mp3",
    now,
  });

  assert.equal(audio.status, "ready");
  assert.equal(audio.url, "/api/segments/segment-1/audio?voice=zh-CN-XiaoxiaoNeural");
  assert.equal("objectKey" in audio, false);
  assert.deepEqual({ ...database.prepare("SELECT object_key, kind, book_id, bytes FROM storage_ledger").get() }, {
    object_key: "audio/book-1/segment-1/v2/zh-CN-XiaoxiaoNeural.mp3",
    kind: "audio",
    book_id: "book-1",
    bytes: 1234,
  });
});

test("lists cached audio for a chapter and ignores stale segment versions", async () => {
  const { d1, database } = await createTestD1();
  seed(database);
  const repository = new AudioRepository(d1);
  await repository.registerReady({
    segmentId: "segment-1", textVersion: 2, voice: "voice", bytes: 3,
    durationMs: 900, objectKey: "audio/current.mp3", now: "2026-08-13T01:00:00.000Z",
  });
  database.exec(`UPDATE segments SET text_version = 3 WHERE id = 'segment-1'`);

  assert.deepEqual(await repository.listChapter("book-1", "chapter-1"), []);
});

test("plans cleanup for the oldest cached books before admitting a sixth", async () => {
  const { d1, database } = await createTestD1();
  seed(database);
  for (let day = 1; day <= 5; day += 1) {
    database.prepare("INSERT INTO books (id, title, format, source_object_key, source_bytes) VALUES (?, ?, 'pdf', ?, 1)")
      .run(`old-${day}`, `旧书 ${day}`, `sources/old-${day}.pdf`);
    database.prepare("INSERT INTO storage_ledger (object_key, kind, book_id, bytes, last_used_at) VALUES (?, 'audio', ?, 100, ?)")
      .run(`audio/old-${day}.mp3`, `old-${day}`, `2026-08-0${day}T00:00:00.000Z`);
  }
  const repository = new AudioRepository(d1);
  assert.deepEqual(await repository.cleanupForAdmission("book-1", 50), {
    allowed: true,
    objectKeys: ["audio/old-1.mp3"],
    evictedBookIds: ["old-1"],
  });
  assert.equal(database.prepare("SELECT COUNT(*) count FROM storage_ledger WHERE object_key = 'audio/old-1.mp3'").get().count, 0);
});
