// @ts-check

import path from 'node:path';

import js from '@eslint/js';
import { globalIgnores } from 'eslint/config';
import formatjs from 'eslint-plugin-formatjs';
import importPlugin from 'eslint-plugin-import';
import jsdoc from 'eslint-plugin-jsdoc';
import jsxA11Y from 'eslint-plugin-jsx-a11y';
import promisePlugin from 'eslint-plugin-promise';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import storybook from 'eslint-plugin-storybook';
import globals from 'globals';
import tseslint from 'typescript-eslint';

/** @type {import('typescript-eslint').ConfigArray} */
export const baseConfig = [
  js.configs.recommended,
  importPlugin.flatConfigs.recommended,
  jsdoc.configs['flat/recommended'],
  promisePlugin.configs['flat/recommended'],
  {
    linterOptions: {
      reportUnusedDisableDirectives: 'error',
      reportUnusedInlineConfigs: 'error',
    },
    rules: {
      'consistent-return': 'error',
      'dot-notation': 'error',

      eqeqeq: [
        'error',
        'always',
        {
          null: 'ignore',
        },
      ],

      'no-console': [
        'warn',
        {
          allow: ['error', 'warn'],
        },
      ],

      'no-empty': [
        'error',
        {
          allowEmptyCatch: true,
        },
      ],

      'no-restricted-properties': [
        'error',
        {
          property: 'substring',
          message: 'Use .slice instead of .substring.',
        },
        {
          property: 'substr',
          message: 'Use .slice instead of .substr.',
        },
      ],

      'no-unused-expressions': 'error',
      'no-unused-vars': 'off',

      'valid-typeof': 'error',

      'import/extensions': [
        'error',
        'always',
        {
          js: 'never',
          jsx: 'never',
          mjs: 'never',
          ts: 'never',
          mts: 'never',
          tsx: 'never',
        },
      ],
      'import/first': 'error',
      'import/newline-after-import': 'error',
      'import/no-anonymous-default-export': 'error',
      'import/no-amd': 'error',
      'import/no-commonjs': 'error',
      'import/no-import-module-exports': 'error',
      'import/no-relative-packages': 'error',
      'import/no-self-import': 'error',
      'import/no-useless-path-segments': 'error',
      'import/order': [
        'error',
        {
          alphabetize: {
            order: 'asc',
          },

          'newlines-between': 'always',

          groups: [
            'builtin',
            'external',
            'internal',
            'parent',
            ['index', 'sibling'],
            'object',
          ],

          pathGroups: [
            {
              pattern: '{react,react-dom,react-dom/client,prop-types}',
              group: 'builtin',
              position: 'after',
            },
            {
              pattern: '{react-intl,intl-messageformat}',
              group: 'builtin',
              position: 'after',
            },
            {
              pattern:
                '{classnames,react-helmet,react-router,react-router-dom}',
              group: 'external',
              position: 'before',
            },
            {
              pattern:
                '{immutable,@reduxjs/toolkit,react-redux,react-immutable-proptypes,react-immutable-pure-component}',
              group: 'external',
              position: 'before',
            },
            {
              pattern: '{mastodon/**}',
              group: 'internal',
              position: 'after',
            },
            {
              pattern: '{flavours/glitch-soc/**}',
              group: 'internal',
              position: 'after',
            },
          ],

          pathGroupsExcludedImportTypes: [],
        },
      ],

      'jsdoc/check-types': 'off',
      'jsdoc/no-undefined-types': 'off',
      'jsdoc/require-jsdoc': 'off',
      'jsdoc/require-param-description': 'off',
      'jsdoc/require-property-description': 'off',
      'jsdoc/require-returns-description': 'off',
      'jsdoc/require-returns': 'off',

      // Forbid imports from vanilla in glitch flavour
      'import/no-restricted-paths': [
        'error',
        {
          zones: [
            {
              target: 'app/javascript/flavours/glitch/',
              from: 'app/javascript/mastodon/',
              message: 'Import from /flavours/glitch/ instead',
            },
          ],
        },
      ],

      'promise/always-return': 'off',
      'promise/catch-or-return': [
        'error',
        {
          allowFinally: true,
        },
      ],
      'promise/no-callback-in-promise': 'off',
      'promise/no-nesting': 'off',
      'promise/no-promise-in-callback': 'off',
    },
  },
];

export default tseslint.config([
  baseConfig,
  globalIgnores([
    'build/**/*',
    'coverage/**/*',
    'db/**/*',
    'lib/**/*',
    'log/**/*',
    'node_modules/**/*',
    'public/**/*',
    '!public/embed.js',
    'spec/**/*',
    'tmp/**/*',
    'vendor/**/*',
    'streaming/**/*',
    '.bundle/**/*',
  ]),
  react.configs.flat.recommended,
  react.configs.flat['jsx-runtime'],
  reactHooks.configs['recommended-latest'],
  jsxA11Y.flatConfigs.recommended,
  importPlugin.flatConfigs.react,
  // @ts-expect-error -- For some reason the formatjs package exports an empty object?
  formatjs.configs.strict,
  storybook.configs['flat/recommended'],
  {
    languageOptions: {
      globals: {
        ...globals.browser,
      },

      parser: tseslint.parser,
      ecmaVersion: 2021,
      sourceType: 'module',
    },

    settings: {
      react: {
        version: 'detect',
      },

      'import/ignore': ['node_modules', '\\.(css|scss|json)$'],

      'import/resolver': {
        typescript: {
          project: path.resolve(import.meta.dirname, './tsconfig.json'),
        },
      },
    },

    rules: {
      'no-restricted-syntax': [
        'error',
        {
          // eslint-disable-next-line no-restricted-syntax
          selector: 'Literal[value=/•/], JSXText[value=/•/]',
          // eslint-disable-next-line no-restricted-syntax
          message: "Use '·' (middle dot) instead of '•' (bullet)",
        },
      ],

      'formatjs/enforce-description': 'off', // description values not currently used
      'formatjs/enforce-id': 'off', // Explicit IDs are used in the project
      'formatjs/enforce-placeholders': 'off', // Issues in short_number.jsx
      'formatjs/no-invalid-icu': 'error',
      'formatjs/no-literal-string-in-jsx': 'off', // Should be looked at, but mainly flagging punctuation outside of strings
      'formatjs/no-multiple-plurals': 'off', // Should be looked at

      'jsx-a11y/click-events-have-key-events': 'off',
      'jsx-a11y/label-has-associated-control': 'off',
      'jsx-a11y/media-has-caption': 'off',
      'jsx-a11y/no-autofocus': 'off',
      'jsx-a11y/no-interactive-element-to-noninteractive-role': 'off',
      'jsx-a11y/no-noninteractive-tabindex': 'off',
      'jsx-a11y/no-static-element-interactions': [
        'warn',
        {
          handlers: ['onClick'],
        },
      ],

      'import/no-extraneous-dependencies': [
        'error',
        {
          devDependencies: [
            'eslint.config.mjs',
            'app/javascript/mastodon/performance.js',
            'app/javascript/testing/**/*',
            'app/javascript/**/__tests__/**',
            'app/javascript/**/*.stories.ts',
            'app/javascript/**/*.stories.tsx',
            'app/javascript/**/*.test.ts',
            'app/javascript/**/*.test.tsx',
            '.storybook/**/*',
          ],
        },
      ],
      'import/no-unresolved': [
        'error',
        {
          ignore: ['vite/modulepreload-polyfill', '^virtual:.+'],
        },
      ],

      'react/jsx-filename-extension': [
        'error',
        {
          extensions: ['.jsx', 'tsx'],
        },
      ],

      'react/jsx-boolean-value': 'error',
      'react/display-name': 'off',
      'react/jsx-fragments': ['error', 'syntax'],
      'react/jsx-equals-spacing': 'error',
      'react/jsx-no-bind': 'error',
      'react/jsx-no-useless-fragment': 'error',
      'react/jsx-no-target-blank': [
        'error',
        {
          allowReferrer: true,
        },
      ],
      'react/jsx-tag-spacing': 'error',
      'react/jsx-wrap-multilines': 'error',
      'react/self-closing-comp': 'error',
    },
  },
  {
    files: [
      'app/javascript/mastodon/common.js',
      'app/javascript/mastodon/features/emoji/unicode_to_unified_name.js',
      'app/javascript/mastodon/features/emoji/emoji_compressed.js',
      'app/javascript/mastodon/features/emoji/unicode_to_filename.js',
      'app/javascript/flavours/glitch/common.js',
      'app/javascript/flavours/glitch/entrypoints/common.js',
      'app/javascript/flavours/glitch/features/emoji/unicode_to_unified_name.js',
      'app/javascript/flavours/glitch/features/emoji/emoji_compressed.js',
      'app/javascript/flavours/glitch/features/emoji/unicode_to_filename.js',
      'app/javascript/mastodon/service_worker/web_push_locales.js',
      '**/*.config.js',
      '**/.*rc.js',
      '**/ide-helper.js',
      'config/formatjs-formatter.js',
    ],

    languageOptions: {
      globals: {
        ...globals.commonjs,
        ...globals.node,
      },

      ecmaVersion: 5,
      sourceType: 'commonjs',
    },

    rules: {
      'import/no-commonjs': 'off',
    },
  },
  {
    files: ['**/*.ts', '**/*.tsx'],

    extends: [
      tseslint.configs.strictTypeChecked,
      tseslint.configs.stylisticTypeChecked,
      react.configs.flat.recommended,
      react.configs.flat['jsx-runtime'],
      reactHooks.configs['recommended-latest'],
      jsxA11Y.flatConfigs.recommended,
      importPlugin.flatConfigs.react,
      importPlugin.flatConfigs.typescript,
      jsdoc.configs['flat/recommended-typescript'],
    ],

    languageOptions: {
      parserOptions: {
        projectService: true,
      },
    },

    rules: {
      // This is not needed as we use noImplicitReturns, which handles this in addition to understanding types
      'consistent-return': 'off',

      'formatjs/enforce-plural-rules': 'off',

      'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
      'import/no-default-export': 'warn',

      'jsdoc/require-jsdoc': 'off',
      'jsdoc/require-param': 'off',
      'jsdoc/require-returns': 'off',

      'react/prefer-stateless-function': 'warn',
      'react/function-component-definition': [
        'error',
        {
          namedComponents: 'arrow-function',
        },
      ],
      'react/prop-types': 'off',

      '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
      '@typescript-eslint/consistent-type-exports': 'error',
      '@typescript-eslint/consistent-type-imports': 'error',
      '@typescript-eslint/prefer-nullish-coalescing': [
        'error',
        {
          ignorePrimitives: {
            boolean: true,
          },
        },
      ],
      '@typescript-eslint/no-restricted-imports': [
        'warn',
        {
          name: 'react-redux',
          importNames: ['useSelector', 'useDispatch'],
          message:
            'Use typed hooks `useAppDispatch` and `useAppSelector` instead.',
        },
      ],
      '@typescript-eslint/no-unused-vars': [
        'error',
        {
          vars: 'all',
          args: 'after-used',
          destructuredArrayIgnorePattern: '^_',
          ignoreRestSiblings: true,
        },
      ],
      '@typescript-eslint/restrict-template-expressions': [
        'warn',
        {
          allowNumber: true,
        },
      ],
    },
  },
  {
    files: ['**/__tests__/*.js', '**/__tests__/*.jsx'],

    languageOptions: {
      globals: globals.vitest,
    },
  },
  {
    files: ['**/*.test.*'],
    rules: {
      'no-global-assign': 'off',
    },
  },
  {
    files: ['**/*.stories.ts', '**/*.stories.tsx', '.storybook/*'],
    rules: {
      'import/no-default-export': 'off',
    },
  },
  {
    files: ['vitest.shims.d.ts'],
    rules: {
      '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off',
      '@typescript-eslint/no-unnecessary-condition': 'off',
      '@typescript-eslint/prefer-nullish-coalescing': 'off',
    },
  },
]);
