import { RuleTester } from 'eslint';
import test from 'node:test';
import rule from '../../src/eslint/rules/no-credentials-spread-in-cache-key.js';

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

const CACHE_FILENAME = '/repo/integrations/foo/src/cache.ts';
const NON_CACHE_FILENAME = '/repo/integrations/foo/src/credentials.ts';

const wrap = (body: string): string => `function buildKey(params, credentials, creds) { ${body} }`;

test('no-credentials-spread-in-cache-key', () => {
  ruleTester.run('no-credentials-spread-in-cache-key', rule, {
    valid: [
      {
        name: 'cache.ts using stable identifier instead of spread',
        filename: CACHE_FILENAME,
        code: wrap(`
          const userIdentifier = credentials.unitoCredentialId ?? credentials.accessToken;
          return crc32(JSON.stringify({ ...params, userIdentifier }));
        `),
      },
      {
        name: 'cache.ts referencing credentials field directly (no spread)',
        filename: CACHE_FILENAME,
        code: wrap(`
          return crc32(JSON.stringify({ ...params, id: credentials.unitoCredentialId }));
        `),
      },
      {
        name: 'cache.ts spreading params is fine',
        filename: CACHE_FILENAME,
        code: wrap(`
          return crc32(JSON.stringify({ ...params }));
        `),
      },
      {
        name: 'cache.ts spreading non-credential identifier',
        filename: CACHE_FILENAME,
        code: wrap(`
          const opts = { foo: 1 };
          return crc32(JSON.stringify({ ...params, ...opts }));
        `),
      },
      {
        name: 'credentials.ts spreading credentials (legitimate enrichment)',
        filename: NON_CACHE_FILENAME,
        code: wrap(`
          return { ...credentials, extra: 'data' };
        `),
      },
      {
        name: 'helpers/credentials.ts file (rule does not target it)',
        filename: '/repo/integrations/foo/src/helpers/credentials.ts',
        code: wrap(`
          return { ...credentials, ...params };
        `),
      },
      {
        name: 'cache.ts spreading credentials member (not the whole object)',
        filename: CACHE_FILENAME,
        code: wrap(`
          return crc32(JSON.stringify({ ...params, ...credentials.publicMetadata }));
        `),
      },
    ],
    invalid: [
      {
        name: 'cache.ts spreading credentials into JSON.stringify',
        filename: CACHE_FILENAME,
        code: wrap(`
          return crc32(JSON.stringify({ ...params, ...credentials })).toString(16);
        `),
        errors: [{ messageId: 'credentialsSpread', data: { name: 'credentials' } }],
      },
      {
        name: 'cache.ts spreading credentials directly inside hash call',
        filename: CACHE_FILENAME,
        code: wrap(`
          return crc.crc32(JSON.stringify({ ...credentials })).toString(16);
        `),
        errors: [{ messageId: 'credentialsSpread', data: { name: 'credentials' } }],
      },
      {
        name: 'cache.ts spreading creds (alias) in cache key',
        filename: CACHE_FILENAME,
        code: wrap(`
          return crc32(JSON.stringify({ ...params, ...creds })).toString(16);
        `),
        errors: [{ messageId: 'credentialsSpread', data: { name: 'creds' } }],
      },
      {
        name: 'cache.ts spreading credentials in helper-built object literal',
        filename: CACHE_FILENAME,
        code: wrap(`
          const input = { prefix, ...credentials, ...params };
          return hash(input);
        `),
        errors: [{ messageId: 'credentialsSpread', data: { name: 'credentials' } }],
      },
      {
        name: 'cache.ts spreading credentials in non-hashing path is still flagged (rule is a hard rule)',
        filename: CACHE_FILENAME,
        code: wrap(`
          const debug = { ...credentials };
          return debug;
        `),
        errors: [{ messageId: 'credentialsSpread', data: { name: 'credentials' } }],
      },
      {
        name: 'cache.ts spreading credentials as call-arg is also flagged',
        filename: CACHE_FILENAME,
        code: wrap(`
          return hash(...credentials);
        `),
        errors: [{ messageId: 'credentialsSpread', data: { name: 'credentials' } }],
      },
      {
        name: 'cache.ts spreading singular alias `cred`',
        filename: CACHE_FILENAME,
        code: `function buildKey(params, cred) {
          return crc32(JSON.stringify({ ...params, ...cred })).toString(16);
        }`,
        errors: [{ messageId: 'credentialsSpread', data: { name: 'cred' } }],
      },
    ],
  });
});
