import { test, expect } from "bun:test";
import { encodeCommand, parseReply, concat } from "../src/resp.ts";

const enc = new TextEncoder();
const dec = new TextDecoder();

test("encodeCommand produces a RESP array of bulk strings", () => {
  const bytes = encodeCommand(["PUBLISH", "order:created", '{"id":7}']);
  expect(dec.decode(bytes)).toBe(
    '*3\r\n$7\r\nPUBLISH\r\n$13\r\norder:created\r\n$8\r\n{"id":7}\r\n',
  );
});

test("parseReply reads a Redis message push", () => {
  const wire = enc.encode("*3\r\n$7\r\nmessage\r\n$5\r\ntopic\r\n$5\r\nhello\r\n");
  const r = parseReply(wire);
  expect(r).not.toBeNull();
  expect(r!.value).toEqual(["message", "topic", "hello"]);
  expect(r!.next).toBe(wire.length);
});

test("parseReply reads a pmessage push (pattern, channel, payload)", () => {
  const wire = enc.encode(
    "*4\r\n$8\r\npmessage\r\n$6\r\norder:*\r\n$13\r\norder:created\r\n$2\r\n{}\r\n".replace(
      "$6\r\norder:*",
      "$7\r\norder:*",
    ),
  );
  const r = parseReply(wire)!;
  expect(r.value).toEqual(["pmessage", "order:*", "order:created", "{}"]);
});

test("parseReply returns null for an incomplete buffer, then completes", () => {
  const full = enc.encode("*1\r\n$5\r\nhello\r\n");
  // Half the bytes → incomplete.
  expect(parseReply(full.subarray(0, 8))).toBeNull();
  // Full → parses.
  expect(parseReply(full)!.value).toEqual(["hello"]);
});

test("parseReply handles integers and simple strings (subscribe confirmations)", () => {
  const sub = enc.encode("*3\r\n$9\r\nsubscribe\r\n$5\r\ntopic\r\n:1\r\n");
  expect(parseReply(sub)!.value).toEqual(["subscribe", "topic", 1]);
  const ok = enc.encode("+OK\r\n");
  expect(parseReply(ok)!.value).toBe("OK");
});

test("concat joins byte chunks in order", () => {
  const joined = concat([enc.encode("ab"), enc.encode("cd")]);
  expect(dec.decode(joined)).toBe("abcd");
});
