import { RuleTester } from 'eslint';
import test from 'node:test';
import rule from '../../src/eslint/rules/no-throw-new-error.js';

const ruleTester = new RuleTester({
  languageOptions: {
    ecmaVersion: 2022,
    sourceType: 'module',
  },
});

const HANDLER_FILE = '/repo/integrations/foo/src/handlers/item.ts';

test('no-throw-new-error', () => {
  ruleTester.run('no-throw-new-error', rule, {
    valid: [
      {
        name: 'throw typed HttpErrors',
        filename: HANDLER_FILE,
        code: `function f() { throw new HttpErrors.NotFoundError('not found'); }`,
      },
      {
        name: 'throw user-defined typed error',
        filename: HANDLER_FILE,
        code: `function f() { throw new ProviderRateLimitError('slow down'); }`,
      },
      {
        name: 'rethrow caught error',
        filename: HANDLER_FILE,
        code: `function f() { try { g(); } catch (e) { throw e; } }`,
      },
      {
        name: 'allowlist: cache.ts may throw raw Error',
        filename: '/repo/integrations/foo/src/cache.ts',
        code: `function hash(v) { if (v === undefined) { throw new Error('Cannot hash undefined value.'); } }`,
      },
      {
        name: 'allowlist: scripts/ may throw raw Error',
        filename: '/repo/integrations/foo/src/scripts/createBatch.ts',
        code: `async function main() { throw new Error('Failed to fetch credentials'); }`,
      },
      {
        name: 'allowlist: script/ (singular) may throw raw Error',
        filename: '/repo/integrations/foo/src/script/credentialsHelper.ts',
        code: `async function main() { throw new Error('No access token found in credential payload'); }`,
      },
      {
        name: 'allowlist: errors.ts (typed error definitions) may throw raw',
        filename: '/repo/integrations/foo/src/errors.ts',
        code: `class FooError extends Error { constructor(m) { super(m); throw new Error('init'); } }`,
      },
      {
        name: 'allowlist: helpers/stringHelper.ts may throw raw Error',
        filename: '/repo/integrations/foo/src/helpers/stringHelper.ts',
        code: `function decode(s) { if (s === undefined) { throw new Error('String to encode is undefined'); } }`,
      },
      {
        name: 'allowlist message: "only supported in tests"',
        filename: HANDLER_FILE,
        code: `async function deleteItem() { throw new Error('Deleting issues is only supported in tests'); }`,
      },
      {
        name: 'allowlist message: assertNever',
        filename: HANDLER_FILE,
        code: `function check(x) { throw new Error('assertNever: ' + x); }`,
      },
      {
        name: 'throw a literal value (not an Error constructor) — out of scope',
        filename: HANDLER_FILE,
        code: `function f() { throw 'plain string'; }`,
      },
      {
        name: 'throw custom subclass of Error',
        filename: HANDLER_FILE,
        code: `class FooError extends Error {}\nfunction f() { throw new FooError('boom'); }`,
      },
      {
        name: 'MemberExpression callee like globalThis.Error is out of scope (edge case)',
        filename: HANDLER_FILE,
        code: `function f() { throw new globalThis.Error('x'); }`,
      },
    ],
    invalid: [
      {
        name: 'throw new Error in handler',
        filename: HANDLER_FILE,
        code: `function f() { throw new Error('Item not found'); }`,
        errors: [{ messageId: 'rawThrow', data: { name: 'Error' } }],
      },
      {
        name: 'throw Error without `new` (still an Error instance)',
        filename: HANDLER_FILE,
        code: `function f() { throw Error('Item not found'); }`,
        errors: [{ messageId: 'rawThrow', data: { name: 'Error' } }],
      },
      {
        name: 'throw new TypeError in handler',
        filename: HANDLER_FILE,
        code: `function f() { throw new TypeError('Invalid input'); }`,
        errors: [{ messageId: 'rawThrow', data: { name: 'TypeError' } }],
      },
      {
        name: 'throw new RangeError in handler',
        filename: HANDLER_FILE,
        code: `function f() { throw new RangeError('out of range'); }`,
        errors: [{ messageId: 'rawThrow', data: { name: 'RangeError' } }],
      },
      {
        name: 'throw new Error w/ template literal does not match allowlist',
        filename: HANDLER_FILE,
        code: 'function f(t) { throw new Error(`Unsupported item type "${t}"`); }',
        errors: [{ messageId: 'rawThrow', data: { name: 'Error' } }],
      },
      {
        name: 'throw new Error w/ template literal — partial allowlist match should NOT exempt',
        filename: HANDLER_FILE,
        code: 'function f(t) { throw new Error(`Unsupported item type "${t}" - only supported in production`); }',
        errors: [{ messageId: 'rawThrow', data: { name: 'Error' } }],
      },
    ],
  });
});
