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

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

const wrap = (body: string): string => `async function f() { ${body} }`;

test('no-full-error-object-logging', () => {
  ruleTester.run('no-full-error-object-logging', rule, {
    valid: [
      {
        name: 'structured field extraction',
        code: wrap('try { foo(); } catch (error) { logger.error({ message: error.message }); }'),
      },
      {
        name: 'string description + structured metadata',
        code: wrap("try { foo(); } catch (error) { logger.error('upload failed', { message: error.message }); }"),
      },
      {
        name: 'just the message string',
        code: wrap('try { foo(); } catch (error) { logger.error(error.message); }'),
      },
      {
        name: 'static string — no error reference',
        code: wrap("logger.error('static string');"),
      },
      {
        name: 'non-error identifier — result does not match error pattern',
        code: wrap('logger.error(result);'),
      },
      {
        name: 'not a logger call — someFunction(error) is ignored',
        code: wrap('try { foo(); } catch (error) { someFunction(error); }'),
      },
      {
        name: 'error.code extraction is fine',
        code: wrap('try { foo(); } catch (error) { logger.error({ code: error.code }); }'),
      },
      {
        name: 'nested function inside catch — function boundary stops scope walk',
        code: wrap(`
          try { foo(); } catch (oops) {
            const handler = () => { logger.error(oops); };
            handler();
          }
        `),
      },
      {
        name: 'catch with destructured param — not an Identifier, tier 1 skips',
        code: wrap('try { foo(); } catch ({ message }) { logger.error(message); }'),
      },
      {
        name: 'scripts/ files are excluded — CLI tooling needs full error output',
        filename: '/repo/integrations/foo/src/scripts/createIssueInBatch.ts',
        code: wrap('try { foo(); } catch (err) { logger.error(err); }'),
      },
    ],
    invalid: [
      {
        name: 'tier 1: catch parameter passed to logger.error',
        code: wrap('try { foo(); } catch (error) { logger.error(error); }'),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'error', identifier: 'error' } }],
      },
      {
        name: 'tier 1: renamed catch parameter',
        code: wrap('try { foo(); } catch (oops) { logger.error(oops); }'),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'error', identifier: 'oops' } }],
      },
      {
        name: 'tier 1: chained logger (context.logger.warn)',
        code: wrap('try { foo(); } catch (e) { context.logger.warn(e); }'),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'warn', identifier: 'e' } }],
      },
      {
        name: 'tier 1: catch param as second argument',
        code: wrap("try { foo(); } catch (error) { logger.error('failed', error); }"),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'error', identifier: 'error' } }],
      },
      {
        name: 'tier 2: error-named identifier outside catch',
        code: wrap('logger.error(err);'),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'error', identifier: 'err' } }],
      },
      {
        name: 'tier 2: logger.warn(error)',
        code: wrap('logger.warn(error);'),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'warn', identifier: 'error' } }],
      },
      {
        name: 'tier 2: logger.error(exception)',
        code: wrap('logger.error(exception);'),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'error', identifier: 'exception' } }],
      },
      {
        name: 'tier 2: logger.info(cause)',
        code: wrap('logger.info(cause);'),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'info', identifier: 'cause' } }],
      },
      {
        name: 'tier 2: console.error(error)',
        code: wrap('console.error(error);'),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'error', identifier: 'error' } }],
      },
      {
        name: 'tier 2: error-named var inside nested function in catch — tier 2 still fires',
        code: wrap(`
          try { foo(); } catch (err) {
            const handler = () => { logger.error(err); };
            handler();
          }
        `),
        errors: [{ messageId: 'fullErrorObjectLogged', data: { method: 'error', identifier: 'err' } }],
      },
    ],
  });
});
