const path = require("path");
const js = require("@eslint/js");
const reactPlugin = require("eslint-plugin-react");
const reactHooksPlugin = require("eslint-plugin-react-hooks");
const tseslint = require("typescript-eslint");
const prettier = require("eslint-config-prettier/flat");
const { FlatCompat } = require("@eslint/eslintrc");
const { fixupConfigRules } = require("@eslint/compat");
const jestPlugin = require("eslint-plugin-jest");
const globals = require("globals");
const spirePlugin = require("eslint-plugin-spire");
const orderedImportsPlugin = require("eslint-plugin-ordered-imports");
const tsParser = require("@typescript-eslint/parser");

const compat = new FlatCompat({
    baseDirectory: __dirname,
});

module.exports = [
    {
        ignores: [
            "blueprintBuilder/**",
            "dist/**",
            "node_modules/**",
            "scripts/**",
            "Icons/**",
            "**/Froalaeditor.d.ts",
            "wwwroot/dist/**",
            "vendor/**",
            "**/vendor/**",
            "**/node_modules/**/*.js",
            "**/*.min.js",
        ],
    },

    // Base JS config
    js.configs.recommended,

    // TypeScript config
    ...tseslint.config({
        extends: [
            ...tseslint.configs.recommended,
            // We should look into using this in the future, but we get way too many errors with it right now.
            // ...tseslint.configs.recommendedTypeChecked
        ],
        languageOptions: {
            parser: tsParser,
            parserOptions: {
                project: ["./tsconfig.base.json", "./modules/*/tsconfig.json"],
                tsconfigRootDir: "./",
                parserOptions: {
                    ecmaFeatures: { jsx: true },
                },
            },
        },
        files: ["**/*.{ts,tsx}"],
        rules: {
            "@typescript-eslint/no-unsafe-call": "off", // Should be "error"; this is a TypeScript-specific rule that should be used to find unsafe calls.
            "@typescript-eslint/no-unnecessary-type-assertion": "off", // Should be "error"; this is a TypeScript-specific rule that should be used to find unnecessary type assertions.
            "@typescript-eslint/no-misused-promises": "off", // Should be "error"; this is a TypeScript-specific rule that should be used to find misused promises.
            "@typescript-eslint/no-unsafe-assignment": "off", // Should be "error"; this is a TypeScript-specific rule that should be used to find unsafe assignments.
            "@typescript-eslint/no-floating-promises": "off", // Should be "error"; this is a TypeScript-specific rule that should be used to find floating promises.
            "@typescript-eslint/no-unsafe-argument": "off", // Should be "error"; this is a TypeScript-specific rule that should be used to find unsafe arguments.
            "@typescript-eslint/no-unsafe-return": "off", // Should be "error"; this is a TypeScript-specific rule that should be used to find unsafe returns.
            "@typescript-eslint/no-unsafe-member-access": "off", // Should be "error"; this is a TypeScript-specific rule that should be used to find unsafe member access.
            "@typescript-eslint/consistent-type-assertions": "off", // Should be "error"; inconsistent pattern used.
            "@typescript-eslint/explicit-function-return-type": "off", // Should be "off"; saves a lot of code to let TypeScript infer returns.
            "@typescript-eslint/member-delimiter-style": "off", // Should be "error"; cosmetic but arguably valid.
            "@typescript-eslint/no-empty-function": "off", // Should be "error"; Empty functions are wasteful.
            "@typescript-eslint/no-empty-interface": "off", // Should be "error"; empty interfaces suggest an architectural problem.
            "@typescript-eslint/no-explicit-any": "off", // Should be "error"; would be often suppressed, but may find some legitimate issues.
            "@typescript-eslint/no-non-null-assertion": "off", // Should be "error"; non-null assertions are risky and should be discouraged.
            "@typescript-eslint/no-this-alias": "off", // Should be "error"; suggests a structural issue.
            "@typescript-eslint/no-unused-vars": "off", // Should be "error"; could potentially find bugs about forgotten parameters.
            "@typescript-eslint/no-unused-expressions": "off", // Should be "error", affected syntax is wasteful and maybe should use `?.`.
            "@typescript-eslint/no-use-before-define": "off", // Should be "error"; cosmetic but sensible for better conceptual flow.
            "@typescript-eslint/semi": "off", // handled by prettier
            "@typescript-eslint/triple-slash-reference": "off", // Should be "error"; will be suppressed occasionally but discourages legacy syntax.
            "@typescript-eslint/type-annotation-spacing": "off", // Should be "error"; inconsistent code style.
            "@typescript-eslint/ban-types": "off",
            "@typescript-eslint/no-empty-object-type": "off", // Turning off to fix TypeScript empty object type errors
            "@typescript-eslint/no-unsafe-function-type": "off", // Turning off Function type warnings
        },
    }),

    reactPlugin.configs.flat.recommended,
    reactPlugin.configs.flat["jsx-runtime"],

    ...fixupConfigRules(
        compat.config({
            extends: "airbnb-base/legacy",
        }),
    ),

    // Prettier config
    prettier,

    // Base Config
    // Note: This config will override any rules set in any configs above
    {
        files: ["**/*.{js,jsx,ts,tsx}"],
        plugins: {
            react: reactPlugin,
            "react-hooks": reactHooksPlugin,
            "ordered-imports": orderedImportsPlugin,
            spire: spirePlugin,
        },
        linterOptions: {
            // The default behavior of ESLint is to have this set as "warn", which I think is the right choice.
            // but there is an issue with spire/avoid-dynamic-translate which causes it to be "error" then when its turned off
            // it shows as a warning.
            reportUnusedInlineConfigs: "off",
        },
        languageOptions: {
            parser: tsParser,
            parserOptions: {
                ecmaFeatures: { jsx: true },
            },
            globals: {
                ...globals.es2018,
                ...globals.browser,
                React: "readonly",
                NodeJS: "readonly",
                __WebpackModuleApi: "readonly",
                AsyncGenerator: "readonly",
                google: "readonly",
                JSX: "readonly",
                BUILD_DATE: "readonly",
                IS_SERVER_SIDE: "readonly",
                IS_PRODUCTION: "readonly",
                BLUEPRINT_NAME: "readonly",
            },
        },
        settings: {
            react: {
                pragma: "React",
                version: "18.0.0",
            },
        },
        rules: {
            "prefer-regex-literals": "off",
            "arrow-parens": "off", // Should be customized; we generally don't use unnecessary parentheses in our C# arrow functions.
            "class-methods-use-this": "off",
            "consistent-return": "off",
            "default-case": "off",
            "dot-notation": "off", // Should be "error"; unnecessary complexity.
            "func-names": "off", // Should be configured for our needs.
            "guard-for-in": "off", // Should be "error"; potential source of bugs.
            "import/first": "off", // Should be "error"; keeps imports in the appropriate place.
            "import/extensions": "off", // Requires special configuration or customization to be usable.
            "import/no-cycle": "off", // Should be "error"; Incredibly slow and does not seem to work with typescript.
            "import/no-duplicates": "off", // slow, and basically the same as no-duplicate-imports
            "import/no-extraneous-dependencies": "off", // the way this is built doesn't require our package.json to be perfect.
            "import/no-mutable-exports": "off", // Should be "error"; may be occasional exceptions but this can lead to erratic behavior by consumers.
            "import/no-self-import": "off", // expensive and highly unlikely
            "import/order": "off", // Should be "error"; inconsistent import ordering makes reading the list harder than necessary.
            "import/prefer-default-export": "off", // Should be "error"; not using a default export where appropriate makes things slightly harder to use.
            "jsx-a11y/accessible-emoji": "off", // Should be "error"; accessibility issue.
            "jsx-a11y/anchor-is-valid": "off", // Should be "error"; accessibility issue.
            "jsx-a11y/click-events-have-key-events": "off", // Should be "error"; accessibility issue.
            "jsx-a11y/control-has-associated-label": "off", // Should be "error"; accessibility issue.
            "jsx-a11y/html-has-lang": "off", // Should be "error"; accessibility issue.
            "jsx-a11y/iframe-has-title": "off", // Should be "error"; accessibility issue.
            "jsx-a11y/label-has-associated-control": "off", // Should be "error"; accessibility issue.
            "jsx-a11y/mouse-events-have-key-events": "off", // Should be "error"; accessibility issue.
            "jsx-a11y/no-noninteractive-element-interactions": "off", // Should be "error"; accessibility issue.
            "jsx-a11y/no-static-element-interactions": "off", // Should be "error"; accessibility issue.
            "no-prototype-builtins": "error",
            "lines-between-class-members": "off", // Debatable, may be usable if customizable for packed fields but spaced functions.
            "max-classes-per-file": "off", // Debatable.
            "no-alert": "error", // We shouldn't be using alerts
            "no-await-in-loop": "off", // Should be "off"; this isn't done unintentionally.
            "no-confusing-arrow": "off", // Debatable.
            "no-constant-binary-expression": "off", // Turning off to fix constant binary expression errors
            "no-continue": "off", // Should be "off"; we're comfortable using `continue`.
            "no-console": "warn",
            "no-empty": "warn", // Changed from error to warning
            "no-fallthrough": "warn", // Changed from error to warning
            "no-func-assign": "off", // Turning off function assignment errors
            "no-loop-func": "off", // Should be "error; complicated code organization.
            "no-misleading-character-class": "off", // Turning off character class errors
            "no-multi-assign": "off", // Debatable.
            "no-nested-ternary": "off", // Should be "error"; code written this way is hard to read.
            "no-param-reassign": "off", // Should be "off" or customized (if feasible); our reducer+immer pattern requires it.
            "no-plusplus": "off", // Arguably should be "off"; if semi-colons are required we're not exposed to ++'s quirks.
            "no-restricted-syntax": "off", // Should be "off"; too restrictive. Maybe customizable to something tolerable.
            "no-shadow": "off", // Should be "error"; can make code confusing to read.
            "no-sparse-arrays": "off", // Turning off sparse array errors
            "no-underscore-dangle": "off", // Should be "error"; bad naming pattern. May occasionally need to be suppressed for 3rd party APIs.
            "no-unused-expressions": "off", // Doesn't support `?.` (at the time this comment was written); @typescript-eslint/no-unused-expressions works.
            "no-unsafe-finally": "off", // Turning off unsafe finally errors
            "no-use-before-define": "off",
            "no-useless-escape": "off", // Should be "error"; unnecessary syntax.
            "no-unused-vars": "off", // Should be "error"; could potentially find bugs about forgotten parameters.
            "operator-assignment": "off", // Should be "error"; unnecessary syntax. Might occasionally be suppressed in edge cases.
            "operator-linebreak": "off", // should be ["error", "before"] but conflicts with prettier
            "ordered-imports/ordered-imports": "off", // Turning off completely - too many warnings
            "prefer-const": "off", // pointless - see https://jamie.build/const
            "prefer-destructuring": "off", // Should be "error"; destructuring is break-even-or-better than direct access for minification.
            "react-hooks/rules-of-hooks": "off", // Changed from error to warning
            "react-hooks/exhaustive-deps": "off", // Should be "error"; indicates hooks aren't being used correctly.
            "require-yield": "off", // Turning off require yield errors
            "valid-typeof": "off", // Turning off valid typeof errors
            "getter-return": "off", // Turning off getter-return errors
            "require-atomic-updates": "off", // Ideally would be "error", but would get triggered frequently by our front-end handler chain design.
            "vars-on-top": "off", // Conflicts with the general ban on `var`.

            // React rules
            "react/no-did-update-set-state": "error",
            "react/no-danger": "error",
            "object-shorthand": "error",
            "react/button-has-type": "off", // Debatable, probably "off" is fine as it should be well-understood what happens with an implicit type.
            "react/default-props-match-prop-types": "off", // does not apply to TS
            "react/destructuring-assignment": "off", // Should be "error"; destructuring is break-even-or-better than direct access for minification.
            "react/display-name": "off", // slow and we don't use createReactClass
            "react/jsx-boolean-value": "off", // Should be "error"; ={true} on a boolean is unnecessary.
            "react/jsx-props-no-spreading": "off", // Should be "error"; leads to confusing code
            "react/no-access-state-in-setstate": "off", // Should be "error"; potential source of bugs.
            "react/no-deprecated": "off", // slow and if we move to react 17 we can clean these up
            "react/no-redundant-should-component-update": "off", // handled by TS
            "react/no-unescaped-entities": "off", // Debatable--probably should be off since unescaped entities are safe and have cleaner code.
            "react/no-unused-prop-types": "off", // Should be "error"; Slow and doesn't seem to work
            "react/no-unused-state": "off", // Should be "error"; potential source of bugs.
            "react/prop-types": "off",
            "react/prefer-es6-class": "off",
            "react/prefer-stateless-function": "off", // Should be "error"; Very slow and can be enforced via code reviews
            "react/require-render-return": "off", // slow and will be obvious
            "react/self-closing-comp": "off", // Should be "error"; wasteful React syntax.
            "react/sort-comp": "off", // Should be "error"; non-standard React component format.
            "react/state-in-constructor": "off", // Should be "error"; non-standard React component format.
            "react/static-property-placement": "off", // Should be "error"; non-standard React component format.
            "react/void-dom-elements-no-children": "off", // slow and will be obvious

            // Spire rules
            "spire/export-chain": "error",
            "spire/export-styles": "error",
            "spire/fenced-imports": ["error", { failRelativeImports: true }],
            "spire/restrict-lodash-import": "warn",
            "spire/no-node-fetch": "error",
            "spire/avoid-dynamic-translate": [
                "error",
                {
                    generateTranslations: !!process.env.GENERATE_TRANSLATIONS,
                    ignoreDir: [
                        "/modules/blueprints",
                        "/modules/blueprints-shell",
                        "/modules/server-framework",
                        "/modules/shell-public",
                        "/modules/spire-linter",
                        "/modules/test",
                    ],
                    translationsLocation: path.resolve(__dirname, "wwwroot/AppData"),
                },
            ],
        },
    },

    // Node files config
    {
        files: ["./*.js", "config/**/*.js", "scripts/**/*.js"],
        languageOptions: {
            globals: {
                ...globals.node,
            },
            sourceType: "commonjs",
        },
        rules: {
            "@typescript-eslint/no-var-requires": "off",
            strict: "off",
            "no-console": "off",
        },
    },

    // Test Config
    {
        files: ["**/*.test.{js,jsx,ts,tsx}"],
        plugins: {
            jest: jestPlugin,
        },
        languageOptions: {
            globals: {
                ...jestPlugin.environments.globals.globals,
            },
        },
        rules: {
            "spire/fenced-imports": "off",
            // We should consider using this in the future.
            // ...jestPlugin.configs.recommended.rules,
        },
    },
];
