import { test } from 'node:test';
import assert from 'node:assert/strict';
import { getSettings } from './settings';

// getSettings() reads localStorage; node has none, so install a controllable
// mock per case. (The module already loaded safely with no localStorage thanks
// to the guarded catch — that path is exercised by the "throws" test below.)
const setStored = (value: string | null): void => {
  (globalThis as { localStorage?: unknown }).localStorage = {
    getItem: () => value,
    setItem: () => undefined,
    removeItem: () => undefined,
  };
};
const setThrowingStorage = (): void => {
  (globalThis as { localStorage?: unknown }).localStorage = {
    getItem: () => {
      throw new Error('storage blocked');
    },
    setItem: () => {
      throw new Error('storage blocked');
    },
    removeItem: () => {
      throw new Error('storage blocked');
    },
  };
};

test('returns defaults when nothing is stored', () => {
  setStored(null);
  assert.equal(getSettings().callNoiseSuppression, 'browser');
});

test('migrates the legacy boolean callNoiseSuppression to the 3-way mode', () => {
  setStored(JSON.stringify({ callNoiseSuppression: true }));
  assert.equal(getSettings().callNoiseSuppression, 'browser');
  setStored(JSON.stringify({ callNoiseSuppression: false }));
  assert.equal(getSettings().callNoiseSuppression, 'off');
  // a new string value passes through untouched
  setStored(JSON.stringify({ callNoiseSuppression: 'ml' }));
  assert.equal(getSettings().callNoiseSuppression, 'ml');
});

test('coerces unknown persisted denoise model / ringtone id back to defaults', () => {
  setStored(null);
  const defaults = getSettings();

  setStored(JSON.stringify({ callDenoiseModel: 'retired-model', ringtoneId: 'bogus' }));
  const coerced = getSettings();
  assert.equal(coerced.callDenoiseModel, defaults.callDenoiseModel);
  assert.equal(coerced.ringtoneId, defaults.ringtoneId);

  setStored(JSON.stringify({ callDenoiseModel: 'rnnoise', ringtoneId: 'chime' }));
  const valid = getSettings();
  assert.equal(valid.callDenoiseModel, 'rnnoise');
  assert.equal(valid.ringtoneId, 'chime');
});

test('merges stored values over defaults', () => {
  setStored(JSON.stringify({ callNoiseSuppression: 'off', someUnknownKey: 1 }));
  const s = getSettings();
  assert.equal(s.callNoiseSuppression, 'off');
  // a default field not present in storage is still populated
  assert.notEqual(s.callDenoiseModel, undefined);
});

test('returns defaults without throwing when localStorage access throws', () => {
  setThrowingStorage();
  // regression: the catch used to call localStorage.removeItem(), which re-threw
  // and crashed the app at module load when storage was blocked.
  assert.doesNotThrow(() => getSettings());
  assert.equal(getSettings().callNoiseSuppression, 'browser');
});

test('returns defaults on malformed JSON', () => {
  setStored('{ not valid json');
  assert.doesNotThrow(() => getSettings());
  assert.equal(getSettings().callNoiseSuppression, 'browser');
});
