/* 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/. */

import { FormLikeFactory } from "resource://gre/modules/FormLikeFactory.sys.mjs";
import { SignUpFormRuleset } from "resource://gre/modules/SignUpFormRuleset.sys.mjs";
import { FirefoxRelayUtils } from "resource://gre/modules/FirefoxRelayUtils.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";

export class FormScenarios {
  /**
   * Caches the scores when running the SignUpFormRuleset against a form
   */
  static #cachedSignUpFormScore = new WeakMap();

  /**
   * Detect usage scenarios of the form.
   *
   * @param {object} options named options
   * @param {HTMLInputElement} [options.input] where current focus is
   * @param {FormLike} [options.form]
   *
   * @returns {Array<string>} detected scenario names
   */
  static detect({ input, form }) {
    const scenarios = {};

    // Running simple heuristics first, because running the SignUpFormRuleset is expensive
    if (
      input &&
      // At the moment Relay integration is the only interested party in "sign up form",
      // so we optimize a bit by checking if it's enabled or not.
      FirefoxRelayUtils.isRelayInterestedField(input)
    ) {
      form ??= FormLikeFactory.findRootForField(input);

      scenarios.signUpForm = FormScenarios.#isProbablyASignUpForm(form);
    }

    return scenarios;
  }

  /**
   * Determine if the form is a sign-up form.
   * This is done by running the rules of the Fathom SignUpFormRuleset against the form and calucating a score between 0 and 1.
   * It's considered a sign-up form, if the score is higher than the confidence threshold (default=0.75)
   *
   * @param {HTMLFormElement} formElement
   * @returns {boolean} returns true if the calculcated score is higher than the confidenceThreshold
   */
  static #isProbablyASignUpForm(formElement) {
    let score = FormScenarios.#cachedSignUpFormScore.get(formElement);
    if (!score) {
      let timerId = Glean.pwmgr.signupFormDetection.start();
      try {
        const { rules, type } = SignUpFormRuleset;
        const results = rules.against(formElement);
        score = results.get(formElement).scoreFor(type);
        Glean.pwmgr.signupFormDetection.stopAndAccumulate(timerId);
        timerId = null;
      } finally {
        if (timerId) {
          Glean.pwmgr.signupFormDetection.cancel(timerId);
        }
      }
      FormScenarios.#cachedSignUpFormScore.set(formElement, score);
    }

    const threshold = FormScenarios.signupDetectionConfidenceThreshold;
    return score > threshold;
  }
}
XPCOMUtils.defineLazyPreferenceGetter(
  FormScenarios,
  "signupDetectionConfidenceThreshold",
  "signon.signupDetection.confidenceThreshold",
  "0.75"
);
