// Copyright 2018-2026 the Deno authors. MIT license.

import { TextLineStream } from "./text_line_stream.ts";
import { assertEquals } from "@std/assert";

Deno.test("TextLineStream parses simple input", async () => {
  const stream = ReadableStream.from([
    "qwertzu",
    "iopasd\r\nmnbvc",
    "xylk\rjhgfds\napoiuzt\r",
    "qwr\r09ei\rqwrjiowqr\r",
    "\nrewq0987\n\n654321",
    "\nrewq0987\r\n\r\n654321\r",
  ]).pipeThrough(new TextLineStream());

  assertEquals(await Array.fromAsync(stream), [
    "qwertzuiopasd",
    "mnbvcxylk\rjhgfds",
    "apoiuzt\rqwr\r09ei\rqwrjiowqr",
    "rewq0987",
    "",
    "654321",
    "rewq0987",
    "",
    "654321\r",
  ]);

  const stream2 = ReadableStream.from(["rewq0987\r\n\r\n654321\n"])
    .pipeThrough(new TextLineStream());

  assertEquals(await Array.fromAsync(stream2), [
    "rewq0987",
    "",
    "654321",
  ]);
});

Deno.test("TextLineStream parses with `allowCR` enabled", async () => {
  const stream = ReadableStream.from([
    "qwertzu",
    "iopasd\r\nmnbvc",
    "xylk\rjhgfds\napoiuzt\r",
    "qwr\r09ei\rqwrjiowqr\r",
    "\nrewq0987\n\n654321",
    "\nrewq0987\r\n\r\n654321\r",
  ]).pipeThrough(new TextLineStream({ allowCR: true }));
  assertEquals(await Array.fromAsync(stream), [
    "qwertzuiopasd",
    "mnbvcxylk",
    "jhgfds",
    "apoiuzt",
    "qwr",
    "09ei",
    "qwrjiowqr",
    "rewq0987",
    "",
    "654321",
    "rewq0987",
    "",
    "654321",
  ]);

  const stream2 = ReadableStream.from(["rewq0987\r\n\r\n654321\n"])
    .pipeThrough(new TextLineStream());

  assertEquals(await Array.fromAsync(stream2), [
    "rewq0987",
    "",
    "654321",
  ]);
});

Deno.test("TextLineStream parses large chunks", async () => {
  const totalLines = 20_000;
  const stream = ReadableStream.from(["\n".repeat(totalLines)])
    .pipeThrough(new TextLineStream());
  const lines = await Array.fromAsync(stream);

  assertEquals(lines.length, totalLines);
  assertEquals(lines, Array.from({ length: totalLines }).fill(""));
});

Deno.test("TextLineStream parses no final empty chunk with terminal newline", async () => {
  const stream = ReadableStream.from([
    "abc\n",
    "def\nghi\njk",
    "l\nmn",
    "o\np",
    "qr",
    "\nstu\nvwx\n",
    "yz\n",
  ]).pipeThrough(new TextLineStream());

  assertEquals(await Array.fromAsync(stream), [
    "abc",
    "def",
    "ghi",
    "jkl",
    "mno",
    "pqr",
    "stu",
    "vwx",
    "yz",
  ]);
});

Deno.test("TextLineStream parses a line spanning many chunks", async () => {
  const longLine = "a".repeat(4096);
  const chunks: string[] = [];
  for (let i = 0; i < longLine.length; i += 7) {
    chunks.push(longLine.slice(i, i + 7));
  }
  chunks.push("\nrest");

  for (const allowCR of [false, true]) {
    const stream = ReadableStream.from(chunks.slice())
      .pipeThrough(new TextLineStream({ allowCR }));
    assertEquals(await Array.fromAsync(stream), [longLine, "rest"]);
  }
});

Deno.test("TextLineStream parses input with empty chunks", async () => {
  const stream = ReadableStream.from(["", "abc\nd", "", "ef", ""])
    .pipeThrough(new TextLineStream());

  assertEquals(await Array.fromAsync(stream), ["abc", "def"]);
});

Deno.test("TextLineStream with `allowCR` parses a chunk-final `\\r` once the next chunk arrives", async () => {
  const stream = ReadableStream.from(["abc\r", "def"])
    .pipeThrough(new TextLineStream({ allowCR: true }));

  assertEquals(await Array.fromAsync(stream), ["abc", "def"]);
});

Deno.test("TextLineStream parses no final empty chunk without terminal newline", async () => {
  const stream = ReadableStream.from([
    "abc\n",
    "def\nghi\njk",
    "l\nmn",
    "o\np",
    "qr",
    "\nstu\nvwx\n",
    "yz",
  ]).pipeThrough(new TextLineStream());

  assertEquals(await Array.fromAsync(stream), [
    "abc",
    "def",
    "ghi",
    "jkl",
    "mno",
    "pqr",
    "stu",
    "vwx",
    "yz",
  ]);
});
