/**
 * @fileoverview Reject common XPCOM methods called with useless optional
 *               parameters, or non-existent parameters.
 *
 * 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/.
 */

"use strict";

module.exports = {
  meta: {
    docs: {
      url: "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/eslint-plugin-mozilla/no-useless-parameters.html",
    },
    fixable: "code",
    messages: {
      newURIParams: "newURI's last parameters are optional.",
      obmittedWhenFalse:
        "{{fnName}}'s {{index}} parameter can be omitted when it's false.",
      onlyTakes: "{{fnName}} only takes {{params}}",
    },
    schema: [],
    type: "suggestion",
  },

  create(context) {
    function getRangeAfterArgToEnd(argNumber, args) {
      let sourceCode = context.getSourceCode();
      return [
        sourceCode.getTokenAfter(args[argNumber]).range[0],
        args[args.length - 1].range[1],
      ];
    }

    return {
      CallExpression(node) {
        let callee = node.callee;
        if (
          callee.type !== "MemberExpression" ||
          callee.property.type !== "Identifier"
        ) {
          return;
        }

        let isFalse = arg => arg.type === "Literal" && arg.value === false;
        let isFalsy = arg => arg.type === "Literal" && !arg.value;
        let isBool = arg =>
          arg.type === "Literal" && (arg.value === false || arg.value === true);
        let name = callee.property.name;
        let args = node.arguments;

        if (
          ["addEventListener", "removeEventListener", "addObserver"].includes(
            name
          ) &&
          args.length === 3 &&
          isFalse(args[2])
        ) {
          context.report({
            node,
            fix: fixer => {
              return fixer.removeRange(getRangeAfterArgToEnd(1, args));
            },
            messageId: "obmittedWhenFalse",
            data: { fnName: name, index: "third" },
          });
        }

        if (name === "clearUserPref" && args.length > 1) {
          context.report({
            node,
            fix: fixer => {
              return fixer.removeRange(getRangeAfterArgToEnd(0, args));
            },
            messageId: "onlyTakes",
            data: { fnName: name, params: "1 parameter" },
          });
        }

        if (name === "removeObserver" && args.length === 3 && isBool(args[2])) {
          context.report({
            node,
            fix: fixer => {
              return fixer.removeRange(getRangeAfterArgToEnd(1, args));
            },
            messageId: "onlyTakes",
            data: { fnName: name, params: "2 parameters" },
          });
        }

        if (name === "appendElement" && args.length === 2 && isFalse(args[1])) {
          context.report({
            node,
            fix: fixer => {
              return fixer.removeRange(getRangeAfterArgToEnd(0, args));
            },
            messageId: "obmittedWhenFalse",
            data: { fnName: name, index: "second" },
          });
        }

        if (
          name === "notifyObservers" &&
          args.length === 3 &&
          isFalsy(args[2])
        ) {
          context.report({
            node,
            fix: fixer => {
              return fixer.removeRange(getRangeAfterArgToEnd(1, args));
            },
            messageId: "obmittedWhenFalse",
            data: { fnName: name, index: "third" },
          });
        }

        if (
          name === "getComputedStyle" &&
          args.length === 2 &&
          isFalsy(args[1])
        ) {
          context.report({
            node,
            fix: fixer => {
              return fixer.removeRange(getRangeAfterArgToEnd(0, args));
            },
            messageId: "obmittedWhenFalse",
            data: { fnName: "getComputedStyle", index: "second" },
          });
        }

        if (
          name === "newURI" &&
          args.length > 1 &&
          isFalsy(args[args.length - 1])
        ) {
          context.report({
            node,
            fix: fixer => {
              if (args.length > 2 && isFalsy(args[args.length - 2])) {
                return fixer.removeRange(getRangeAfterArgToEnd(0, args));
              }

              return fixer.removeRange(
                getRangeAfterArgToEnd(args.length - 2, args)
              );
            },
            messageId: "newURIParams",
          });
        }
      },
    };
  },
};
