import assert from "node:assert/strict";
import { open, unlink } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { randomUUID } from "node:crypto";
import { encodeGraphPath, GraphClient, UPLOAD_CHUNK_SIZE } from "./graph.js";
import type { AccessToken, TokenProvider } from "./types.js";

const tokenProvider: TokenProvider = {
  async getToken(): Promise<AccessToken> {
    return { accessToken: "graph-token", expiresAt: Date.now() + 60_000 };
  },
};

test("encodeGraphPath preserves folders and escapes each segment", () => {
  assert.equal(
    encodeGraphPath("Account 1/file #1.txt"),
    "Account%201/file%20%231.txt",
  );
});

test("large upload uses sequential 10 MiB content ranges", async () => {
  const filePath = join(tmpdir(), `sharepoint-worker-${randomUUID()}.bin`);
  const totalSize = UPLOAD_CHUNK_SIZE * 2 + 17;
  const file = await open(filePath, "w", 0o600);
  await file.truncate(totalSize);
  await file.close();

  const ranges: string[] = [];
  const fetchMock: typeof fetch = async (input, init) => {
    const url = String(input);
    if (url.includes("createUploadSession")) {
      return Response.json({ uploadUrl: "https://upload.example/session" });
    }
    assert.equal(url, "https://upload.example/session");
    ranges.push(new Headers(init?.headers).get("content-range") ?? "");
    if (ranges.length < 3) {
      return Response.json({ nextExpectedRanges: [`${ranges.length * UPLOAD_CHUNK_SIZE}-`] }, { status: 202 });
    }
    return Response.json({
      id: "drive-item-id",
      size: totalSize,
      webUrl: "https://example.sharepoint.com/file.bin",
    }, { status: 201 });
  };

  try {
    const graph = new GraphClient(tokenProvider, "site-id", fetchMock);
    const item = await graph.upload(filePath, totalSize, "Account/file.bin");
    assert.equal(item.id, "drive-item-id");
    assert.deepEqual(ranges, [
      `bytes 0-${UPLOAD_CHUNK_SIZE - 1}/${totalSize}`,
      `bytes ${UPLOAD_CHUNK_SIZE}-${UPLOAD_CHUNK_SIZE * 2 - 1}/${totalSize}`,
      `bytes ${UPLOAD_CHUNK_SIZE * 2}-${totalSize - 1}/${totalSize}`,
    ]);
  } finally {
    await unlink(filePath).catch(() => undefined);
  }
});
