import { RemoteL10n, _RemoteL10n } from "lib/RemoteL10n.sys.mjs";
import { GlobalOverrider } from "test/unit/utils";

describe("RemoteL10n", () => {
  let sandbox;
  let globals;
  let domL10nStub;
  let l10nRegStub;
  let l10nRegInstance;
  let fileSourceStub;
  beforeEach(() => {
    sandbox = sinon.createSandbox();
    globals = new GlobalOverrider();
    domL10nStub = sandbox.stub();
    l10nRegInstance = {
      hasSource: sandbox.stub(),
      registerSources: sandbox.stub(),
      removeSources: sandbox.stub(),
    };

    fileSourceStub = sandbox.stub();
    l10nRegStub = {
      getInstance: () => {
        return l10nRegInstance;
      },
    };
    globals.set("DOMLocalization", domL10nStub);
    globals.set("L10nRegistry", l10nRegStub);
    globals.set("L10nFileSource", fileSourceStub);
  });
  afterEach(() => {
    sandbox.restore();
    globals.restore();
  });
  describe("#RemoteL10n", () => {
    it("should create a new instance", () => {
      assert.ok(new _RemoteL10n());
    });
    it("should create a DOMLocalization instance", () => {
      domL10nStub.returns({ instance: true });
      const instance = new _RemoteL10n();

      assert.propertyVal(instance._createDOML10n(), "instance", true);
      assert.calledOnce(domL10nStub);
    });
    it("should create a new instance", () => {
      domL10nStub.returns({ instance: true });
      const instance = new _RemoteL10n();

      assert.ok(instance.l10n);

      instance.reloadL10n();

      assert.ok(instance.l10n);

      assert.calledTwice(domL10nStub);
    });
    it("should reuse the instance", () => {
      domL10nStub.returns({ instance: true });
      const instance = new _RemoteL10n();

      assert.ok(instance.l10n);
      assert.ok(instance.l10n);

      assert.calledOnce(domL10nStub);
    });
  });
  describe("#_createDOML10n", () => {
    it("should load the remote Fluent file if USE_REMOTE_L10N_PREF is true", async () => {
      sandbox.stub(global.Services.prefs, "getBoolPref").returns(true);
      l10nRegInstance.hasSource.returns(false);
      RemoteL10n._createDOML10n();

      assert.calledOnce(domL10nStub);
      const { args } = domL10nStub.firstCall;
      // The first arg is the resource array,
      // the second one is false (use async),
      // and the third one is the bundle generator.
      assert.equal(args.length, 2);
      assert.deepEqual(args[0], [
        "branding/brand.ftl",
        "browser/defaultBrowserNotification.ftl",
        "browser/newtab/asrouter.ftl",
        "toolkit/branding/accounts.ftl",
        "toolkit/branding/brandings.ftl",
      ]);
      assert.isFalse(args[1]);
      assert.calledOnce(l10nRegInstance.hasSource);
      assert.calledOnce(l10nRegInstance.registerSources);
      assert.notCalled(l10nRegInstance.removeSources);
    });
    it("should load the local Fluent file if USE_REMOTE_L10N_PREF is false", () => {
      sandbox.stub(global.Services.prefs, "getBoolPref").returns(false);
      l10nRegInstance.hasSource.returns(true);
      RemoteL10n._createDOML10n();

      const { args } = domL10nStub.firstCall;
      // The first arg is the resource array,
      // the second one is false (use async),
      // and the third one is null.
      assert.equal(args.length, 2);
      assert.deepEqual(args[0], [
        "branding/brand.ftl",
        "browser/defaultBrowserNotification.ftl",
        "browser/newtab/asrouter.ftl",
        "toolkit/branding/accounts.ftl",
        "toolkit/branding/brandings.ftl",
      ]);
      assert.isFalse(args[1]);
      assert.calledOnce(l10nRegInstance.hasSource);
      assert.notCalled(l10nRegInstance.registerSources);
      assert.calledOnce(l10nRegInstance.removeSources);
    });
  });
  describe("#createElement", () => {
    let doc;
    let instance;
    let setStringStub;
    let elem;
    beforeEach(() => {
      elem = document.createElement("div");
      doc = {
        createElement: sandbox.stub().returns(elem),
        createElementNS: sandbox.stub().returns(elem),
      };
      instance = new _RemoteL10n();
      setStringStub = sandbox.stub(instance, "setString");
    });
    it("should call createElement if string_id is defined", () => {
      instance.createElement(doc, "span", { content: { string_id: "foo" } });

      assert.calledOnce(doc.createElement);
    });
    it("should call createElementNS if string_id is not present", () => {
      instance.createElement(doc, "span", { content: "foo" });

      assert.calledOnce(doc.createElementNS);
    });
    it("should set classList", () => {
      instance.createElement(doc, "span", { classList: "foo" });

      assert.isTrue(elem.classList.contains("foo"));
    });
    it("should call setString", () => {
      const options = { classList: "foo" };
      instance.createElement(doc, "span", options);

      assert.calledOnce(setStringStub);
      assert.calledWithExactly(setStringStub, elem, options);
    });
  });
  describe("#setString", () => {
    let instance;
    beforeEach(() => {
      instance = new _RemoteL10n();
    });
    it("should set fluent variables and id", () => {
      let el = { setAttribute: sandbox.stub() };
      instance.setString(el, {
        content: { string_id: "foo" },
        attributes: { bar: "bar", baz: "baz" },
      });

      assert.calledThrice(el.setAttribute);
      assert.calledWithExactly(el.setAttribute, "fluent-variable-bar", "bar");
      assert.calledWithExactly(el.setAttribute, "fluent-variable-baz", "baz");
      assert.calledWithExactly(el.setAttribute, "fluent-remote-id", "foo");
    });
    it("should set content if no string_id", () => {
      let el = { setAttribute: sandbox.stub() };
      instance.setString(el, { content: "foo" });

      assert.notCalled(el.setAttribute);
      assert.equal(el.textContent, "foo");
    });
  });
  describe("#isLocaleSupported", () => {
    it("should return true if the locale is en-US", () => {
      assert.ok(RemoteL10n.isLocaleSupported("en-US"));
    });
    it("should return true if the locale is in all-locales", () => {
      assert.ok(RemoteL10n.isLocaleSupported("en-CA"));
    });
    it("should return false if the locale is not in all-locales", () => {
      assert.ok(!RemoteL10n.isLocaleSupported("und"));
    });
  });
  describe("#formatLocalizableText", () => {
    let instance;
    let formatValueStub;
    beforeEach(() => {
      instance = new _RemoteL10n();
      formatValueStub = sandbox.stub();
      sandbox
        .stub(instance, "l10n")
        .get(() => ({ formatValue: formatValueStub }));
    });
    it("should localize a string_id", async () => {
      formatValueStub.resolves("VALUE");

      assert.equal(
        await instance.formatLocalizableText({ string_id: "ID" }),
        "VALUE"
      );
      assert.calledOnce(formatValueStub);
    });
    it("should pass through a string", async () => {
      formatValueStub.reset();

      assert.equal(
        await instance.formatLocalizableText("unchanged"),
        "unchanged"
      );
      assert.isFalse(formatValueStub.called);
    });
  });
});
