import assert from "node:assert";
import { describe, test } from "node:test";
import BrowserFileTree from "../../src/drivers/BrowserFileTree.js";
import { Tree } from "../../src/internal.js";

// Skip these tests if we're not in a browser.
const isBrowser = typeof window !== "undefined";
if (isBrowser) {
  describe("BrowserFileTree", async () => {
    test("can get the keys of the tree", async () => {
      const fixture = await createFixture();
      assert.deepEqual(Array.from(await fixture.keys()), [
        "Alice.md",
        "Bob.md",
        "Carol.md",
        "subfolder/",
      ]);
    });

    test("can get the value for a key", async () => {
      const fixture = await createFixture();
      const buffer = await fixture.get("Alice.md");
      assert.equal(text(buffer), "Hello, **Alice**.");
    });

    test("getting an unsupported key returns undefined", async () => {
      const fixture = await createFixture();
      assert.equal(await fixture.get("xyz"), undefined);
    });

    test("getting empty key returns undefined", async () => {
      const fixture = await createFixture();
      assert.equal(await fixture.get(""), undefined);
    });

    test("getting a null/undefined key throws an exception", async () => {
      const fixture = await createFixture();
      await assert.rejects(async () => {
        await fixture.get(null);
      });
      await assert.rejects(async () => {
        await fixture.get(undefined);
      });
    });

    test("sets parent on subtrees", async () => {
      const fixture = await createFixture();
      const subfolder = await fixture.get("subfolder");
      assert.equal(subfolder.parent, fixture);
    });

    test("can retrieve values with optional trailing slash", async () => {
      const fixture = await createFixture();
      assert(await fixture.get("Alice.md"));
      assert(await fixture.get("Alice.md/"));
      assert(await fixture.get("subfolder"));
      assert(await fixture.get("subfolder/"));
    });

    test("can set a value", async () => {
      const fixture = await createFixture();

      // Update existing key.
      await fixture.set("Alice.md", "Goodbye, **Alice**.");

      // New key.
      await fixture.set("David.md", "Hello, **David**.");

      // Delete key.
      await fixture.set("Bob.md", undefined);

      // Delete non-existent key.
      await fixture.set("xyz", undefined);

      assert.deepEqual(await strings(fixture), {
        "Alice.md": "Goodbye, **Alice**.",
        "Carol.md": "Hello, **Carol**.",
        "David.md": "Hello, **David**.",
        subfolder: {},
      });
    });

    test("can create a subfolder via set", async () => {
      const fixture = await createFixture();
      const tree = {
        async get(key) {
          const name = key.replace(/\.md$/, "");
          return `Hello, **${name}**.`;
        },
        async keys() {
          return ["Ellen.md"];
        },
      };
      await fixture.set("more", tree);
      assert.deepEqual(await strings(fixture), {
        "Alice.md": "Hello, **Alice**.",
        "Bob.md": "Hello, **Bob**.",
        "Carol.md": "Hello, **Carol**.",
        more: {
          "Ellen.md": "Hello, **Ellen**.",
        },
        subfolder: {},
      });
    });
  });
}

async function createFile(directory, name, contents) {
  const file = await directory.getFileHandle(name, { create: true });
  const writable = await file.createWritable();
  await writable.write(contents);
  await writable.close();
}

let count = 0;
async function createFixture() {
  const root = await navigator.storage.getDirectory();
  const directory = await root.getDirectoryHandle("async-tree", {
    create: true,
  });

  // Create a new subdirectory for each test.
  const subdirectoryName = `test${count++}`;

  // Delete any pre-existing subdirectory with that name.
  try {
    await directory.removeEntry(subdirectoryName, { recursive: true });
  } catch (e) {
    // Ignore errors.
  }

  const subdirectory = await directory.getDirectoryHandle(subdirectoryName, {
    create: true,
  });

  await createFile(subdirectory, "Alice.md", "Hello, **Alice**.");
  await createFile(subdirectory, "Bob.md", "Hello, **Bob**.");
  await createFile(subdirectory, "Carol.md", "Hello, **Carol**.");

  await subdirectory.getDirectoryHandle("subfolder", {
    create: true,
  });

  return new BrowserFileTree(subdirectory);
}

async function strings(tree) {
  return Tree.plain(Tree.map(tree, (value) => text(value)));
}

function text(arrayBuffer) {
  return new TextDecoder().decode(arrayBuffer);
}
