/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Tests for validating tool definitions in Tools.sys.mjs
 *
 * The tests are designed to verify that tool definitions are in compliance
 *   with the OpenAI API standard.
 *
 * These tests ensure that:
 * 1. Tool definitions have the correct structure
 * 2. Parameters match what the actual functions accept
 * 3. Required fields are present and correctly specified
 * 4. Parameter types are valid and consistent
 */

do_get_profile();

const { toolsConfig, TOOLS } = ChromeUtils.importESModule(
  "moz-src:///browser/components/aiwindow/models/Tools.sys.mjs"
);

/**
 * Test that all tool definitions have the required top-level structure
 */
add_task(function test_toolDefinitions_structure() {
  Assert.ok(Array.isArray(toolsConfig), "toolsConfig should be an array");
  Assert.equal(
    toolsConfig.length,
    TOOLS.length,
    "toolsConfig length should match TOOLS length"
  );

  for (const [index, toolDef] of toolsConfig.entries()) {
    Assert.equal(
      toolDef.type,
      "function",
      `Tool ${index} should have type "function"`
    );
    Assert.ok(
      toolDef.function,
      `Tool ${index} should have a function property`
    );
    Assert.equal(
      typeof toolDef.function.name,
      "string",
      `Tool ${index} function.name should be a string`
    );
    Assert.greater(
      toolDef.function.name.length,
      0,
      `Tool ${index} function.name should not be empty`
    );
    Assert.equal(
      typeof toolDef.function.description,
      "string",
      `Tool ${index} function.description should be a string`
    );
    Assert.greater(
      toolDef.function.description.length,
      0,
      `Tool ${index} function.description should not be empty`
    );
    Assert.ok(
      toolDef.function.parameters,
      `Tool ${index} should have parameters`
    );
    Assert.equal(
      toolDef.function.parameters.type,
      "object",
      `Tool ${index} parameters.type should be "object"`
    );
  }
});

/**
 * Test that all tool names in TOOLS array match the tool definitions
 */
add_task(function test_toolDefinitions_names_match_constants() {
  const definedNames = toolsConfig.map(t => t.function.name);
  const expectedNames = TOOLS;

  Assert.deepEqual(
    definedNames.sort(),
    expectedNames.sort(),
    "Tool names in toolsConfig should match TOOLS constant"
  );
});

/**
 * Test that all parameter types are valid JSON Schema types
 */
add_task(function test_parameter_types_are_valid() {
  const validTypes = [
    "string",
    "number",
    "integer",
    "boolean",
    "array",
    "object",
    "null",
  ];

  for (const toolDef of toolsConfig) {
    const props = toolDef.function.parameters.properties;

    for (const [paramName, paramDef] of Object.entries(props)) {
      Assert.ok(
        validTypes.includes(paramDef.type),
        `Parameter ${paramName} in ${toolDef.function.name} should have valid JSON Schema type`
      );

      // If it's an array, check items type
      if (paramDef.type === "array" && paramDef.items) {
        Assert.ok(
          validTypes.includes(paramDef.items.type),
          `Array items type for ${paramName} in ${toolDef.function.name} should be valid`
        );
      }
    }
  }
});

/**
 * Test that all parameters have non-empty descriptions
 */
add_task(function test_parameter_descriptions_are_meaningful() {
  for (const toolDef of toolsConfig) {
    const props = toolDef.function.parameters.properties;

    for (const [paramName, paramDef] of Object.entries(props)) {
      Assert.greater(
        paramDef.description.length,
        10,
        `Parameter ${paramName} in ${toolDef.function.name} should have meaningful description (>10 chars)`
      );

      // Check nested items descriptions for arrays
      if (paramDef.type === "array" && paramDef.items?.description) {
        Assert.greater(
          paramDef.items.description.length,
          10,
          `Array items description for ${paramName} in ${toolDef.function.name} should be meaningful`
        );
      }
    }
  }
});

/**
 * Test that required parameters are actually defined in properties
 */
add_task(function test_required_parameters_exist_in_properties() {
  for (const toolDef of toolsConfig) {
    const params = toolDef.function.parameters;
    if (params.required) {
      Assert.ok(
        Array.isArray(params.required),
        `Tool ${toolDef.function.name} required field should be an array if present`
      );
      for (const requiredParam of params.required) {
        Assert.ok(
          params.properties[requiredParam],
          `Required parameter ${requiredParam} in ${toolDef.function.name} should exist in properties`
        );
      }
    }
  }
});
