"use strict";

const { Schemas } = ChromeUtils.importESModule(
  "resource://gre/modules/Schemas.sys.mjs"
);

const global = this;

let schemaJson = [
  {
    namespace: "noAllowedContexts",
    properties: {
      prop1: { type: "object" },
      prop2: { type: "object", allowedContexts: ["test_zero", "test_one"] },
      prop3: { type: "number", value: 1 },
      prop4: { type: "number", value: 1, allowedContexts: ["numeric_one"] },
    },
  },
  {
    namespace: "defaultContexts",
    defaultContexts: ["test_two"],
    properties: {
      prop1: { type: "object" },
      prop2: { type: "object", allowedContexts: ["test_three"] },
      prop3: { type: "number", value: 1 },
      prop4: { type: "number", value: 1, allowedContexts: ["numeric_two"] },
    },
  },
  {
    namespace: "withAllowedContexts",
    allowedContexts: ["test_four"],
    properties: {
      prop1: { type: "object" },
      prop2: { type: "object", allowedContexts: ["test_five"] },
      prop3: { type: "number", value: 1 },
      prop4: { type: "number", value: 1, allowedContexts: ["numeric_three"] },
    },
  },
  {
    namespace: "withAllowedContextsAndDefault",
    allowedContexts: ["test_six"],
    defaultContexts: ["test_seven"],
    properties: {
      prop1: { type: "object" },
      prop2: { type: "object", allowedContexts: ["test_eight"] },
      prop3: { type: "number", value: 1 },
      prop4: { type: "number", value: 1, allowedContexts: ["numeric_four"] },
    },
  },
  {
    namespace: "with_submodule",
    defaultContexts: ["test_nine"],
    types: [
      {
        id: "subtype",
        type: "object",
        functions: [
          {
            name: "noAllowedContexts",
            type: "function",
            parameters: [],
          },
          {
            name: "allowedContexts",
            allowedContexts: ["test_ten"],
            type: "function",
            parameters: [],
          },
        ],
      },
    ],
    properties: {
      prop1: { $ref: "subtype" },
      prop2: { $ref: "subtype", allowedContexts: ["test_eleven"] },
    },
  },
];

add_task(async function testRestrictions() {
  let url = "data:," + JSON.stringify(schemaJson);
  await Schemas.load(url);
  let results = {};
  let localWrapper = {
    manifestVersion: 2,
    cloneScope: global,
    shouldInject(ns, name, allowedContexts) {
      name = ns ? ns + "." + name : name;
      results[name] = allowedContexts.join(",");
      return true;
    },
    getImplementation() {
      // The actual implementation is not significant for this test.
      // Let's take this opportunity to see if schema generation is free of
      // exceptions even when somehow getImplementation does not return an
      // implementation.
    },
  };

  let root = {};
  Schemas.inject(root, localWrapper);

  function verify(path, expected) {
    let obj = root;
    for (let thing of path.split(".")) {
      try {
        obj = obj[thing];
      } catch (e) {
        // Blech.
      }
    }

    let result = results[path];
    equal(result, expected, path);
  }

  verify("noAllowedContexts", "");
  verify("noAllowedContexts.prop1", "");
  verify("noAllowedContexts.prop2", "test_zero,test_one");
  verify("noAllowedContexts.prop3", "");
  verify("noAllowedContexts.prop4", "numeric_one");

  verify("defaultContexts", "");
  verify("defaultContexts.prop1", "test_two");
  verify("defaultContexts.prop2", "test_three");
  verify("defaultContexts.prop3", "test_two");
  verify("defaultContexts.prop4", "numeric_two");

  verify("withAllowedContexts", "test_four");
  verify("withAllowedContexts.prop1", "");
  verify("withAllowedContexts.prop2", "test_five");
  verify("withAllowedContexts.prop3", "");
  verify("withAllowedContexts.prop4", "numeric_three");

  verify("withAllowedContextsAndDefault", "test_six");
  verify("withAllowedContextsAndDefault.prop1", "test_seven");
  verify("withAllowedContextsAndDefault.prop2", "test_eight");
  verify("withAllowedContextsAndDefault.prop3", "test_seven");
  verify("withAllowedContextsAndDefault.prop4", "numeric_four");

  verify("with_submodule", "");
  verify("with_submodule.prop1", "test_nine");
  verify("with_submodule.prop1.noAllowedContexts", "test_nine");
  verify("with_submodule.prop1.allowedContexts", "test_ten");
  verify("with_submodule.prop2", "test_eleven");
  // Note: test_nine inherits allowed contexts from the namespace, not from
  // submodule. There is no "defaultContexts" for submodule types to not
  // complicate things.
  verify("with_submodule.prop1.noAllowedContexts", "test_nine");
  verify("with_submodule.prop1.allowedContexts", "test_ten");

  // This is a constant, so it does not matter that getImplementation does not
  // return an implementation since the API injector should take care of it.
  equal(root.noAllowedContexts.prop3, 1);

  Assert.throws(
    () => root.noAllowedContexts.prop1,
    /undefined/,
    "Should throw when the implementation is absent."
  );
});
