'use strict';
const common = require('../common');
const tmpdir = require('../common/tmpdir');
const assert = require('node:assert');
const { join } = require('node:path');
const { readdir } = require('node:fs/promises');
const { test, describe } = require('node:test');
const { spawnPromisified } = common;
let cnt = 0;

tmpdir.refresh();

function nextLocalStorage() {
  return join(tmpdir.path, `${++cnt}.localstorage`);
}

test('disabled without --experimental-webstorage', async () => {
  for (const api of ['Storage', 'localStorage', 'sessionStorage']) {
    const cp = await spawnPromisified(process.execPath, ['-e', api]);

    assert.strictEqual(cp.code, 1);
    assert.strictEqual(cp.signal, null);
    assert.strictEqual(cp.stdout, '');
    assert(cp.stderr.includes(`ReferenceError: ${api} is not defined`));
  }
});

test('emits a warning when used', async () => {
  for (const api of ['Storage', 'localStorage', 'sessionStorage']) {
    const cp = await spawnPromisified(process.execPath, [
      '--experimental-webstorage',
      '--localstorage-file', nextLocalStorage(),
      '-e', api,
    ]);

    assert.strictEqual(cp.code, 0);
    assert.strictEqual(cp.signal, null);
    assert.strictEqual(cp.stdout, '');
    assert.match(cp.stderr, /ExperimentalWarning: Web Storage/);
  }
});

test('Storage instances cannot be created in userland', async () => {
  const cp = await spawnPromisified(process.execPath, [
    '--experimental-webstorage', '-e', 'new globalThis.Storage()',
  ]);

  assert.strictEqual(cp.code, 1);
  assert.strictEqual(cp.signal, null);
  assert.strictEqual(cp.stdout, '');
  assert.match(cp.stderr, /Error: Illegal constructor/);
});

test('sessionStorage is not persisted', async () => {
  let cp = await spawnPromisified(process.execPath, [
    '--experimental-webstorage', '-pe', 'sessionStorage.foo = "barbaz"',
  ]);
  assert.strictEqual(cp.code, 0);
  assert.match(cp.stdout, /barbaz/);

  cp = await spawnPromisified(process.execPath, [
    '--experimental-webstorage', '-pe', 'sessionStorage.foo',
  ]);
  assert.strictEqual(cp.code, 0);
  assert.match(cp.stdout, /undefined/);
  assert.strictEqual((await readdir(tmpdir.path)).length, 0);
});

test('localStorage throws without --localstorage-file ', async () => {
  const cp = await spawnPromisified(process.execPath, [
    '--experimental-webstorage',
    '-pe', 'localStorage === globalThis.localStorage',
  ]);
  assert.strictEqual(cp.code, 1);
  assert.strictEqual(cp.signal, null);
  assert.strictEqual(cp.stdout, '');
  assert.match(cp.stderr, /The argument '--localstorage-file' is an invalid localStorage location/);
});

test('localStorage is not persisted if it is unused', async () => {
  const cp = await spawnPromisified(process.execPath, [
    '--experimental-webstorage',
    '--localstorage-file', nextLocalStorage(),
    '-pe', 'localStorage === globalThis.localStorage',
  ]);
  assert.strictEqual(cp.code, 0);
  assert.match(cp.stdout, /true/);
  assert.strictEqual((await readdir(tmpdir.path)).length, 0);
});

test('localStorage is persisted if it is used', async () => {
  const localStorageFile = nextLocalStorage();
  let cp = await spawnPromisified(process.execPath, [
    '--experimental-webstorage',
    '--localstorage-file', localStorageFile,
    '-pe', 'localStorage.foo = "barbaz"',
  ]);
  assert.strictEqual(cp.code, 0);
  assert.match(cp.stdout, /barbaz/);
  const entries = await readdir(tmpdir.path);
  assert.strictEqual(entries.length, 1);
  assert.match(entries[0], /\d+\.localstorage/);

  cp = await spawnPromisified(process.execPath, [
    '--experimental-webstorage',
    '--localstorage-file', localStorageFile,
    '-pe', 'localStorage.foo',
  ]);
  assert.strictEqual(cp.code, 0);
  assert.match(cp.stdout, /barbaz/);
});


describe('webstorage quota for localStorage and sessionStorage', () => {
  const MAX_STORAGE_SIZE = 10 * 1024 * 1024;

  test('localStorage can store and retrieve a max of 10 MB quota', async () => {
    const localStorageFile = nextLocalStorage();
    const cp = await spawnPromisified(process.execPath, [
      '--experimental-webstorage',
      '--localstorage-file', localStorageFile,
      // Each character is 2 bytes
      '-pe', `
      localStorage['a'.repeat(${MAX_STORAGE_SIZE} / 2)] = '';
      console.error('filled');
      localStorage.anything = 'should fail';
      `,
    ]);

    assert.match(cp.stderr, /filled/);
    assert.match(cp.stderr, /QuotaExceededError: Setting the value exceeded the quota/);
  });

  test('sessionStorage can store a max of 10 MB quota', async () => {
    const cp = await spawnPromisified(process.execPath, [
      '--experimental-webstorage',
      // Each character is 2 bytes
      '-pe', `sessionStorage['a'.repeat(${MAX_STORAGE_SIZE} / 2)] = '';
      console.error('filled');
      sessionStorage.anything = 'should fail';
      `,
    ]);

    assert.match(cp.stderr, /filled/);
    assert.match(cp.stderr, /QuotaExceededError/);
  });
});
