import { defineConfig, globalIgnores } from "@eslint/config-helpers";
import type { RulesConfig } from "@eslint/core";
import js from "@eslint/js";
import markdown from "@eslint/markdown";
import commentsPlugin from "@eslint-community/eslint-plugin-eslint-comments";
import stylistic from "@stylistic/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
import love from "eslint-config-love";
import { flatConfigs as importPluginsFlatConfigs } from "eslint-plugin-import-x";
import perfectionist from "eslint-plugin-perfectionist";
import prettier from "eslint-plugin-prettier/recommended";
import promise from "eslint-plugin-promise";
import eslintPluginUnicorn from "eslint-plugin-unicorn";
import { configs as ymlConfigs } from "eslint-plugin-yml";
import { configs as tseslintConfigs } from "typescript-eslint";

interface CompatibleConfig {
    name?: string;
    rules?: object;
}

type CompatibleConfigArray = CompatibleConfig[];

function flattenRules(configArray: CompatibleConfigArray): RulesConfig {
    return Object.fromEntries(
        configArray.flatMap((group) => {
            return Object.entries(group.rules ?? {});
        }),
    );
}

const sharedRules: RulesConfig = {
    "arrow-body-style": ["error", "always"],
    complexity: ["off"],
    curly: ["error", "all"],
    eqeqeq: ["error", "always"],
    "no-alert": ["off"],
    "no-console": ["off"],
    "max-len": ["off"],
    "max-nested-callbacks": ["off"],
    "no-extra-semi": ["off"],
    "no-param-reassign": ["off"],
    "no-restricted-imports": [
        "error",
        {
            patterns: [".*"],
        },
    ],
    "no-restricted-syntax": ["error", "DebuggerStatement", "LabeledStatement", "WithStatement"],
    "no-shadow": ["error"],
    "no-underscore-dangle": ["off"],
    "no-unused-expressions": ["error"],
    "no-unused-vars": ["off"],
    "no-useless-constructor": ["off"],
    "object-shorthand": ["error", "always"],
    "prefer-template": ["error"],
    "require-await": ["error"],
    "sort-imports": [
        "error",
        {
            ignoreDeclarationSort: true,
        },
    ],

    "sort-keys": ["off"],

    // conflicts with arrow-body-style "always"
    "unicorn/consistent-arrow-return-style": ["off"],
    "unicorn/no-array-sort": ["off"],
    "unicorn/no-null": ["off"],
    // Temporal is not available in Node 24
    "unicorn/prefer-temporal": ["off"],
    "unicorn/prefer-ternary": ["off"],

    "import-x/extensions": [
        "error",
        "ignorePackages",
        {
            json: "always",
            ts: "never",
            tsx: "never",
        },
    ],
    "import-x/newline-after-import": ["error"],
    "import-x/no-cycle": ["off"],
    "import-x/no-extraneous-dependencies": ["off"],
    "import-x/no-relative-packages": ["error"],
    "import-x/no-unresolved": ["error"],
    "import-x/order": [
        "error",
        {
            alphabetize: { caseInsensitive: true, order: "asc" },
            "newlines-between": "always-and-inside-groups",
        },
    ],
    "import-x/prefer-default-export": ["off"],
};

const config: ReturnType<typeof defineConfig> = defineConfig(
    globalIgnores([".local/*"]),
    {
        ignores: ["dist/**", "reports/**", "coverage/**"],
    },
    {
        files: ["**/*.js", "**/*.cjs", "**/*.mjs", "**/*.ts", "**/*.tsx"],
        extends: [
            js.configs.recommended,
            importPluginsFlatConfigs.recommended,
            importPluginsFlatConfigs.typescript,
            eslintPluginUnicorn.configs.all,
            prettier,
        ],
    },
    {
        files: ["**/*.js", "**/*.cjs", "**/*.mjs", "**/*.ts", "**/*.tsx"],
        languageOptions: {
            parser: tsParser,
            parserOptions: {
                ecmaVersion: "latest",
                projectService: {
                    allowDefaultProject: ["*.md/*.ts"],
                },
                sourceType: "module", // Allows for the use of imports
                tsconfigRootDir: import.meta.dirname,
            },
        },
        settings: {
            "import-x/resolver": {
                node: {},
                typescript: {
                    alwaysTryTypes: true,
                },
            },
        },
        rules: {
            ...sharedRules,
        },
    },
    {
        files: ["**/*.ts", "**/*.tsx"],
        plugins: {
            ...love.plugins,
            "@stylistic/ts": stylistic,
            "eslint-comments": commentsPlugin,
            perfectionist,
        },
        extends: [promise.configs["flat/recommended"]],
        rules: {
            ...flattenRules(tseslintConfigs.strictTypeCheckedOnly),

            ...flattenRules(tseslintConfigs.stylisticTypeCheckedOnly),

            ...love.rules,

            ...sharedRules,

            "no-restricted-imports": ["off"],

            "@stylistic/ts/no-extra-semi": ["error"],

            "@typescript-eslint/consistent-type-imports": [
                "error",
                {
                    disallowTypeAnnotations: false,
                    fixStyle: "separate-type-imports",
                    prefer: "type-imports",
                },
            ], // different than love
            "@typescript-eslint/prefer-destructuring": ["off"],
            "@typescript-eslint/explicit-member-accessibility": ["error"],
            "@typescript-eslint/explicit-module-boundary-types": ["error"],
            "@typescript-eslint/member-ordering": ["error"],
            "@typescript-eslint/no-empty-object-type": ["error"], // stricter than love
            "@typescript-eslint/no-explicit-any": ["error", { fixToUnknown: true, ignoreRestArgs: false }], // stricter than love
            "@typescript-eslint/no-extraneous-class": ["error"], // stricter than love
            "@typescript-eslint/no-magic-numbers": ["off"],
            "@typescript-eslint/no-shadow": ["error"],
            "@typescript-eslint/no-unused-expressions": [
                "error",
                {
                    allowShortCircuit: false,
                    allowTaggedTemplates: false,
                    allowTernary: false,
                    enforceForJSX: false,
                },
            ], // stricter than love
            "@typescript-eslint/no-unused-vars": [
                "error",
                {
                    args: "all",
                    argsIgnorePattern: "^_",
                    caughtErrors: "all",
                    ignoreRestSiblings: false,
                    vars: "all",
                },
            ], // different than love
            "@typescript-eslint/parameter-properties": ["error"],
            "@typescript-eslint/promise-function-async": ["off"],

            "@typescript-eslint/restrict-template-expressions": [
                "error",
                {
                    allowNumber: true,
                },
            ],

            "@typescript-eslint/return-await": ["error", "in-try-catch"],

            "@typescript-eslint/require-await": ["error"],

            "import-x/consistent-type-specifier-style": ["error", "prefer-top-level"],

            // note that this CAN change the outcome of type resolution
            "perfectionist/sort-intersection-types": ["error"],
            "perfectionist/sort-union-types": ["error"],

            "unicorn/class-reference-in-static-methods": ["error", { preferThis: false, preferSuper: false }],

            "unicorn/consistent-class-member-order": ["off"],

            // const go in top
            "unicorn/no-declarations-before-early-exit": ["off"],

            // this one doesn't consider types (e.g. `ip.is4() ? ip.to4().address : ip.address`, the `address` is not of the same type, yet it hits this rule)
            "unicorn/prefer-minimal-ternary": ["off"],

            // type order relies on perfectionist/sort-intersection-types
            "unicorn/prefer-type-literal-last": ["off"],
        },
    },
    {
        extends: [tseslintConfigs.disableTypeChecked],
        files: ["**/*.mjs"],
    },
    markdown.configs.processor,
    {
        files: ["**/*.md/*.yaml", "**/*.md/*.yml"],
        extends: [ymlConfigs["flat/recommended"], ymlConfigs["flat/prettier"]],
    },
    {
        files: ["**/*.md/*.ts"],
        rules: {
            "unicorn/filename-case": ["off"],
        },
    },
);

export default config;
