// @ts-check
import { createRequire } from 'node:module'
import eslint from '@eslint/js'
import pluginN from 'eslint-plugin-n'
import pluginImportX from 'eslint-plugin-import-x'
import pluginRegExp from 'eslint-plugin-regexp'
import tseslint from 'typescript-eslint'
import { defineConfig } from 'eslint/config'
import globals from 'globals'

const require = createRequire(import.meta.url)
const pkgVite = require('./packages/vite/package.json')

// Some rules work better with typechecking enabled, but as enabling it is slow,
// we only do so when linting in IDEs for now. If you want to lint with typechecking
// explicitly, set this to `true` manually.
const shouldTypeCheck = typeof process.env.VSCODE_PID === 'string'

export default defineConfig(
  {
    ignores: [
      'packages/create-vite/template-*',
      '**/dist/**',
      '**/fixtures/**',
      '**/playground-temp/**',
      '**/temp/**',
      '**/.vitepress/cache/**',
      '**/*.snap',
    ],
  },
  eslint.configs.recommended,
  ...tseslint.configs.recommended,
  ...tseslint.configs.stylistic,
  pluginRegExp.configs['flat/recommended'],
  {
    name: 'main',
    languageOptions: {
      parser: tseslint.parser,
      parserOptions: {
        sourceType: 'module',
        ecmaVersion: 2022,
        isolatedDeclarations: true,
        project: shouldTypeCheck
          ? [
              './packages/*/tsconfig.json',
              './packages/vite/src/*/tsconfig.json',
            ]
          : undefined,
      },
      globals: {
        ...globals.es2023,
        ...globals.node,
      },
    },
    settings: {
      node: {
        version: '^20.19.0 || >=22.12.0',
      },
    },
    plugins: {
      n: pluginN,
      'import-x': pluginImportX,
    },
    rules: {
      'n/no-exports-assign': 'error',
      'n/no-unpublished-bin': 'error',
      'n/no-unsupported-features/es-builtins': 'error',
      'n/no-unsupported-features/node-builtins': [
        'error',
        {
          // TODO: remove this when we don't support Node 20 anymore
          ignores: ['Response', 'Request', 'fetch'],
        },
      ],
      'n/process-exit-as-throw': 'error',
      'n/hashbang': 'error',

      eqeqeq: ['warn', 'always', { null: 'never' }],
      'no-debugger': ['error'],
      'no-empty': ['warn', { allowEmptyCatch: true }],
      'no-process-exit': 'off',
      'prefer-const': [
        'warn',
        {
          destructuring: 'all',
        },
      ],

      'n/no-missing-require': [
        'error',
        {
          // for try-catching yarn pnp
          allowModules: ['pnpapi', 'vite'],
          tryExtensions: ['.ts', '.js', '.jsx', '.tsx', '.d.ts'],
        },
      ],
      'n/no-extraneous-import': [
        'error',
        {
          allowModules: [
            'vite',
            'esbuild',
            'rolldown',
            'less',
            'sass',
            'sass-embedded',
            'terser',
            'lightningcss',
            'vitest',
            'unbuild',
          ],
        },
      ],
      'n/no-extraneous-require': [
        'error',
        {
          allowModules: ['vite'],
        },
      ],
      'n/prefer-node-protocol': 'error',

      '@typescript-eslint/ban-ts-comment': 'error',
      '@typescript-eslint/no-unsafe-function-type': 'off',
      '@typescript-eslint/explicit-module-boundary-types': [
        'error',
        { allowArgumentsExplicitlyTypedAsAny: true },
      ],
      '@typescript-eslint/no-empty-function': [
        'error',
        { allow: ['arrowFunctions'] },
      ],
      '@typescript-eslint/no-empty-object-type': [
        'error',
        { allowInterfaces: 'with-single-extends' },
      ],
      '@typescript-eslint/no-empty-interface': 'off',
      '@typescript-eslint/no-explicit-any': 'off',
      'no-extra-semi': 'off',
      '@typescript-eslint/no-extra-semi': 'off', // conflicts with prettier
      '@typescript-eslint/no-inferrable-types': 'off',
      '@typescript-eslint/no-unused-vars': [
        'error',
        {
          args: 'all',
          argsIgnorePattern: '^_',
          caughtErrors: 'all',
          caughtErrorsIgnorePattern: '^_',
          destructuredArrayIgnorePattern: '^_',
          varsIgnorePattern: '^_',
          ignoreRestSiblings: true,
        },
      ],
      '@typescript-eslint/no-require-imports': 'off',
      '@typescript-eslint/consistent-type-imports': [
        'error',
        { prefer: 'type-imports', disallowTypeAnnotations: false },
      ],
      // disable rules set in @typescript-eslint/stylistic which conflict with current code
      // we should discuss if we want to enable these as they encourage consistent code
      '@typescript-eslint/array-type': 'off',
      '@typescript-eslint/consistent-type-definitions': 'off',
      '@typescript-eslint/prefer-for-of': 'off',
      '@typescript-eslint/prefer-function-type': 'off',

      'import-x/no-duplicates': 'error',
      'import-x/order': [
        'error',
        {
          groups: [
            'builtin',
            'external',
            'internal',
            'parent',
            'sibling',
            'index',
          ],
        },
      ],
      'sort-imports': [
        'error',
        {
          ignoreDeclarationSort: true,
        },
      ],

      'regexp/prefer-regexp-exec': 'error',
      'regexp/prefer-regexp-test': 'error',
      // in some cases using explicit letter-casing is more performant than the `i` flag
      'regexp/use-ignore-case': 'off',
    },
  },
  {
    name: 'vite/globals',
    files: ['packages/**/*.?([cm])[jt]s?(x)'],
    ignores: ['**/__tests__/**'],
    rules: {
      'no-restricted-globals': ['error', 'require', '__dirname', '__filename'],
    },
  },
  {
    name: 'vite/node',
    files: ['packages/vite/src/node/**/*.?([cm])[jt]s?(x)'],
    rules: {
      'no-console': ['error'],
      'n/no-restricted-require': [
        'error',
        Object.keys(pkgVite.devDependencies).map((d) => ({
          name: d,
          message:
            `devDependencies can only be imported using ESM syntax so ` +
            `that they are included in the rolldown bundle. If you are trying to ` +
            `lazy load a dependency, use (await import('dependency')).default instead.`,
        })),
      ],
    },
  },
  {
    name: 'playground/enforce-esm',
    files: ['playground/**/*.?([cm])[jt]s?(x)'],
    ignores: [
      'playground/ssr-resolve/**',
      'playground/**/*{commonjs,cjs}*/**',
      'playground/**/*{commonjs,cjs}*',
      'playground/**/*dep*/**',
      'playground/resolve/browser-module-field2/index.web.js',
      'playground/resolve/browser-field/**',
    ],
    rules: {
      'import-x/no-commonjs': 'error',
    },
  },
  {
    name: 'tests',
    files: ['**/__tests__/**/*.?([cm])[jt]s?(x)'],
    rules: {
      'n/no-unsupported-features/node-builtins': [
        'error',
        {
          // ideally we would like to allow all experimental features
          // https://github.com/eslint-community/eslint-plugin-n/issues/199
          ignores: ['fetch', 'import.meta.dirname'],
        },
      ],
    },
  },
  {
    name: 'configs',
    files: [
      'packages/create-vite/tsdown.config.ts',
      'packages/plugin-legacy/tsdown.config.ts',
    ],
    rules: {
      'n/no-unsupported-features/node-builtins': [
        'error',
        {
          // ideally we would like to allow all experimental features
          // https://github.com/eslint-community/eslint-plugin-n/issues/199
          ignores: ['fetch', 'import.meta.dirname'],
        },
      ],
    },
  },

  {
    name: 'disables/vite/client',
    files: ['packages/vite/src/client/**/*.?([cm])[jt]s?(x)'],
    ignores: ['**/__tests__/**'],
    rules: {
      'n/no-unsupported-features/node-builtins': 'off',
    },
  },
  {
    name: 'disables/vite/types',
    files: [
      'packages/vite/src/types/**/*.?([cm])[jt]s?(x)',
      'packages/vite/scripts/**/*.?([cm])[jt]s?(x)',
      '**/*.spec.ts',
    ],
    rules: {
      'n/no-extraneous-import': 'off',
    },
  },
  {
    name: 'disables/vite/cjs',
    files: ['packages/vite/index.cjs'],
    rules: {
      'no-restricted-globals': 'off',
      'n/no-missing-require': 'off',
    },
  },
  {
    name: 'disables/create-vite/templates',
    files: [
      'packages/create-vite/template-*/**/*.?([cm])[jt]s?(x)',
      '**/build.config.ts',
    ],
    rules: {
      'no-undef': 'off',
      'n/no-missing-import': 'off',
      'n/no-extraneous-import': 'off',
      'n/no-extraneous-require': 'off',
      '@typescript-eslint/explicit-module-boundary-types': 'off',
    },
  },
  {
    name: 'disables/playground',
    files: ['playground/**/*.?([cm])[jt]s?(x)', 'docs/**/*.?([cm])[jt]s?(x)'],
    rules: {
      'n/no-extraneous-import': 'off',
      'n/no-extraneous-require': 'off',
      'n/no-missing-import': 'off',
      'n/no-missing-require': 'off',
      'n/no-unsupported-features/es-builtins': 'off',
      'n/no-unsupported-features/node-builtins': 'off',
      '@typescript-eslint/explicit-module-boundary-types': 'off',
      '@typescript-eslint/no-unused-expressions': 'off',
      '@typescript-eslint/no-unused-vars': 'off',
      'no-undef': 'off',
      'no-empty': 'off',
      'no-constant-condition': 'off',
      '@typescript-eslint/no-empty-function': 'off',
    },
  },
  {
    name: 'disables/playground/tsconfig-json',
    files: [
      'playground/tsconfig-json/**/*.?([cm])[jt]s?(x)',
      'playground/tsconfig-json-load-error/**/*.?([cm])[jt]s?(x)',
    ],
    ignores: ['**/__tests__/**'],
    rules: {
      '@typescript-eslint/ban-ts-comment': 'off',
    },
  },
  {
    name: 'disables/js',
    files: ['**/*.js', '**/*.mjs', '**/*.cjs'],
    rules: {
      '@typescript-eslint/explicit-module-boundary-types': 'off',
    },
  },
  {
    name: 'disables/dts',
    files: ['**/*.d.ts'],
    rules: {
      '@typescript-eslint/consistent-indexed-object-style': 'off',
      '@typescript-eslint/triple-slash-reference': 'off',
    },
  },
  {
    name: 'disables/test',
    files: ['**/__tests__/**/*.?([cm])[jt]s?(x)'],
    rules: {
      'no-console': 'off',
      '@typescript-eslint/ban-ts-comment': 'off',
    },
  },
  {
    name: 'disables/typechecking',
    files: [
      '**/*.js',
      '**/*.mjs',
      '**/*.cjs',
      '**/*.d.ts',
      '**/*.d.cts',
      '**/__tests__/**',
      'docs/**',
      'playground/**',
      'scripts/**',
      'vitest.config.ts',
      'vitest.config.e2e.ts',
    ],
    languageOptions: {
      parserOptions: {
        project: false,
      },
    },
  },
)
