import nextCoreWebVitals from 'eslint-config-next/core-web-vitals';
import prettier from 'eslint-config-prettier/flat';

const eslintConfig = [
  ...nextCoreWebVitals,
  prettier,
  {
    // Scope the typescript-eslint rule overrides to TS/TSX files. Without
    // the `files` filter, eslint flat-config attempts to apply these
    // rules to every walked file (including root-level JS / mjs / json
    // configs) and fails because the typescript-eslint plugin only
    // registers itself for TS/TSX. Surfaced 2026-05-14 when CI's
    // `pnpm lint` command ran across the whole repo root.
    files: ['**/*.ts', '**/*.tsx'],
    rules: {
      '@typescript-eslint/no-explicit-any': 'error',
      '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
      // React Compiler safety rules shipped with eslint-config-next@16 /
      // react-hooks@7. Triage status (2026-05-13 sweep):
      //   purity, set-state-in-render, immutability, refs,
      //   set-state-in-effect — promoted to error after the cleanup
      //   sweep (Wave 3 of the 2026-05-12 audit). All hits migrated to
      //   either useQuery, render-phase derivation, key-based remount,
      //   or a justified eslint-disable for canonical setState-on-
      //   subscription patterns. New regressions block CI.
      //   incompatible-library — informational only ("Compiler
      //   skipped this file because of a non-Compiler-safe import").
      //   No action needed; silenced to keep `pnpm lint` output
      //   actionable.
      'react-hooks/purity': 'error',
      'react-hooks/set-state-in-render': 'error',
      'react-hooks/immutability': 'error',
      'react-hooks/refs': 'error',
      'react-hooks/set-state-in-effect': 'error',
      'react-hooks/incompatible-library': 'off',
      // Icon-only buttons must carry a label that screen readers can
      // surface — either an explicit `aria-label`, an `aria-labelledby`,
      // a `title`, or a visible-but-sr-only text child. Catches the
      // pattern where a `<button><Trash2 /></button>` ships with no
      // accessible name. Default Next config enables this at `error`;
      // we keep it loud so new code doesn't regress.
      'jsx-a11y/control-has-associated-label': [
        'warn',
        {
          labelAttributes: ['label'],
          controlComponents: ['Button'],
          ignoreElements: ['audio', 'canvas', 'embed', 'input', 'textarea', 'tr', 'video'],
          ignoreRoles: [
            'grid',
            'listbox',
            'menu',
            'menubar',
            'radiogroup',
            'row',
            'tablist',
            'toolbar',
            'tree',
            'treegrid',
          ],
          depth: 5,
        },
      ],
    },
  },
  {
    // User-facing copy in src/components and src/app should never use
    // em-dashes (—) in JSX text. The user reads em-dashes as a
    // tell-tale "AI-generated" marker; we prefer periods, commas, or
    // simple hyphens. Code comments / audit-log strings / templates
    // outside these directories are exempt.
    //
    // Same rule block also nudges new code toward CSS logical properties
    // (ms-/me-/ps-/pe-/text-start/text-end/border-s/border-e) instead of
    // physical Tailwind utilities. RTL isn't a roadmap requirement today,
    // but every new ml-/mr-/pl-/pr-/text-left/text-right we accept now
    // is a class we'd have to migrate later. Existing 1,000+ sites stay
    // untouched (warn-only). Inline `// eslint-disable-next-line` when
    // the directional intent is truly physical (e.g. a chevron icon).
    files: ['src/components/**/*.tsx', 'src/app/**/*.tsx'],
    rules: {
      // Both selectors share `warn` severity because the RTL nudge is
      // grandfathered (1,000+ existing sites use ml-/mr-/etc). The
      // em-dash sweep cleared every existing instance (2026-05-21), so
      // `warn` still effectively gates new code — it just doesn't break
      // CI on grandfathered RTL utilities. Inline
      // `// eslint-disable-next-line no-restricted-syntax` when the
      // directional intent is truly physical.
      'no-restricted-syntax': [
        'warn',
        {
          selector: "JSXText[value=/\\u2014/]",
          message:
            'No em-dash in user-facing JSX text. Use period, comma, or hyphen instead.',
        },
        {
          selector:
            "JSXAttribute[name.name='className'] > Literal[value=/(?:^|[\\s:])(?:ml-|mr-|pl-|pr-|text-left|text-right|border-l\\b|border-r\\b|rounded-l-|rounded-r-)/]",
          message:
            'Prefer CSS logical properties (ms-/me-/ps-/pe-/text-start/text-end/border-s/border-e/rounded-s-/rounded-e-) over physical directional Tailwind utilities. Existing code is grandfathered; new code should default to logical so a future RTL pass is bounded.',
        },
      ],
    },
  },
  {
    // Tests assert response shape via expect() — narrowing every
    // `res.json()` to a structural type adds boilerplate without catching
    // bugs. Allow `any` casts at JSON boundaries in test files. Also
    // relax unused-vars to warn (destructured-but-unused helpers are
    // common in setup/teardown patterns).
    files: ['tests/**/*.ts', 'tests/**/*.tsx'],
    rules: {
      '@typescript-eslint/no-explicit-any': 'off',
      '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
    },
  },
  {
    ignores: [
      'client-portal/**',
      'next-env.d.ts',
      // Agent worktree artifacts — not part of the canonical tree.
      '.claude/**',
      // Build output + Next generated types
      '.next/**',
      'dist/**',
      // Other sub-projects with their own toolchains
      'website/**',
    ],
  },
];

export default eslintConfig;
