{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "standard",
    "prettier"
  ],
  "env": {
    "es2020": true
  },
  "settings": {
    // Tell eslint-plugin-react to detect which version of React we are using
    "react": {
      "version": "detect"
    }
  },
  "rules": {
    // do not allow importing of implicit dependencies.
    "import/no-extraneous-dependencies": "error",

    // disable some TypeScript rules
    "@typescript-eslint/no-var-requires": "off",
    "@typescript-eslint/no-unused-vars": "off",
    "@typescript-eslint/no-empty-function": "off",
    "@typescript-eslint/no-explicit-any": "off",
    "@typescript-eslint/no-this-alias": "off",
    "@typescript-eslint/no-non-null-assertion": "off",
    "@typescript-eslint/ban-ts-comment": "off"
  },
  "overrides": [
    // NOTE: changing paths may require updating them in the Makefile too.
    {
      // Node
      "files": [
        "**/app/src/**/*.js",
        "app.js",
        "i18next-scanner.config.js",
        "karma.conf.js",
        "scripts/**/*.js",
        "webpack.config*.js"
      ],
      "env": {
        "node": true
      }
    },
    {
      // Test specific rules
      "files": ["**/test/**/*.*"],
      "plugins": [
        "mocha",
        "chai-expect",
        "chai-friendly"
      ],
      "env": {
        "mocha": true
      },
      "rules": {
        // mocha-specific rules
        "mocha/handle-done-callback": "error",
        "mocha/no-exclusive-tests": "error",
        "mocha/no-global-tests": "error",
        "mocha/no-identical-title": "error",
        "mocha/no-nested-tests": "error",
        "mocha/no-pending-tests": "error",
        "mocha/no-skipped-tests": "error",
        "mocha/no-mocha-arrows": "error",


        // Swap the no-unused-expressions rule with a more chai-friendly one
        "no-unused-expressions": "off",
        "chai-friendly/no-unused-expressions": "error",

        // chai-specific rules
        "chai-expect/missing-assertion": "error",
        "chai-expect/terminating-properties": "error",

        // prefer-arrow-callback applies to all callbacks, not just ones in mocha tests.
        // we don't enforce this at the top-level - just in tests to manage `this` scope
        // based on mocha's context mechanism
        "mocha/prefer-arrow-callback": "error"
      }
    },
    {
      // Frontend test specific rules
      "files": ["**/test/karma/**/*.js"],
      "globals": {
        "expect": true,
        "$": true
      }
    },
    {
      // Backend specific rules
      "files": ["**/app/src/**/*.js", "app.js"],
      "rules": {
        // don't allow console.log in backend code
        "no-console": "error",

        // do not allow importing of implicit dependencies.
        "import/no-extraneous-dependencies": ["error", {
          // do not allow importing of devDependencies.
          "devDependencies": false
        }]
      }
    },
    {
      // Cypress specific rules
      "files": ["cypress/**/*.{js,ts,tsx}", "**/test/frontend/**/*.spec.{js,ts,tsx}"],
      "extends": [
        "plugin:cypress/recommended"
      ]
    },
    {
      // React component specific rules
      //
      "files": ["**/frontend/js/**/components/**/*.{js,ts,tsx}", "**/frontend/js/**/hooks/**/*.{js,ts,tsx}"],
      "rules": {
        // https://astexplorer.net/
        "no-restricted-syntax": [
          "error",
          // prohibit direct calls to methods of window.location
          {
            "selector": "CallExpression[callee.object.object.name='window'][callee.object.property.name='location']",
            "message": "Modify location via useLocation instead of calling window.location methods directly"
          },
          // prohibit assignment to window.location
          {
            "selector": "AssignmentExpression[left.object.name='window'][left.property.name='location']",
            "message": "Modify location via useLocation instead of calling window.location methods directly"
          },
          // prohibit assignment to window.location.href
          {
            "selector": "AssignmentExpression[left.object.object.name='window'][left.object.property.name='location'][left.property.name='href']",
            "message": "Modify location via useLocation instead of calling window.location methods directly"
          }
        ]
      }
    },
    {
      // Frontend specific rules
      "files": ["**/frontend/js/**/*.{js,ts,tsx}", "**/frontend/stories/**/*.{js,ts,tsx}", "**/*.stories.{js,ts,tsx}", "**/test/frontend/**/*.{js,ts,tsx}", "**/test/frontend/components/**/*.spec.{js,ts,tsx}"],
      "env": {
        "browser": true
      },
      "parserOptions": {
        "sourceType": "module",
        "ecmaFeatures": {
          "jsx": true
        }
      },
      "plugins": [
        "jsx-a11y"
      ],
      "extends": [
        "plugin:react/recommended",
        "plugin:react-hooks/recommended",
        "plugin:jsx-a11y/recommended",
        "standard-jsx",
        "prettier"
      ],
      "globals": {
        "__webpack_public_path__": true,
        "$": true,
        "angular": true,
        "ace": true,
        "ga": true,
        "sl_console": true,
        "sl_debugging": true,
        // Injected in layout.pug
        "user_id": true,
        "ExposedSettings": true
      },
      "rules": {
        // TODO: remove once https://github.com/standard/eslint-config-standard-react/issues/68 (support eslint@8) is fixed.
        // START: inline standard-react rules
        // "react/jsx-no-bind": ["error", {
        //   "allowArrowFunctions": true,
        //   "allowBind": false,
        //   "ignoreRefs": true
        // },],
        "react/no-did-update-set-state": "error",
        "react/no-unknown-property": "error",
        "react/no-unused-prop-types": "error",
        "react/prop-types": "error",
        // "react/react-in-jsx-scope": "error",
        // END: inline standard-react rules

        "react/jsx-no-target-blank": ["error", {
          "allowReferrer": true
        }],
        // Prevent usage of legacy string refs
        "react/no-string-refs": "error",

        // Prevent curly braces around strings (as they're unnecessary)
        "react/jsx-curly-brace-presence": ["error", {
          "props": "never",
          "children": "never"
        }],

        // Don't import React for JSX; the JSX runtime is added by a Babel plugin
        "react/react-in-jsx-scope": "off",
        "react/jsx-uses-react": "off",

        // Allow functions as JSX props
        "react/jsx-no-bind": "off", // TODO: fix occurrences and re-enable this

        // Fix conflict between prettier & standard by overriding to prefer
        // double quotes
        "jsx-quotes": ["error", "prefer-double"],

        // Override weird behaviour of jsx-a11y label-has-for (says labels must be
        // nested *and* have for/id attributes)
        "jsx-a11y/label-has-for": [
          "error",
          {
            "required": {
              "some": [
                "nesting",
                "id"
              ]
            }
          }
        ]
      }
    },
    // React + TypeScript-specific rules
    {
      "files": ["**/*.tsx"],
      "rules": {
        "react/prop-types": "off",
        "no-undef": "off"
      }
    },
    // TypeScript-specific rules
    {
      "files": ["**/*.ts"],
      "rules": {
        "no-undef": "off"
      }
    },
    {
      "files": ["scripts/ukamf/*.js"],
      "rules": {
        // Do not allow importing of any dependencies unless specified in either
        //  - web/package.json
        //  - web/scripts/ukamf/package.json
        "import/no-extraneous-dependencies": ["error", {"packageDir": [".", "scripts/ukamf"]}]
      }
    },
    {
      "files": ["scripts/learn/checkSanitize/*.js"],
      "rules": {
        // The checkSanitize script is used in the dev-env only.
        "import/no-extraneous-dependencies": ["error", {
          "devDependencies": true,
          "packageDir": [".", "../../"]
        }]
      }
    }
  ]
}
