import { readFileSync, readdirSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { plainToInstance } from 'class-transformer';

const sourceRoot = resolve(__dirname);
const declarationSuffixes = [
  '.decorator.ts',
  '.dto.ts',
  '.enum.ts',
  '.guard.ts',
  '.interface.ts',
  '.module.ts',
  '.schema.ts',
  '.strategy.ts',
];

function collectDeclarationFiles(directory: string): string[] {
  return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
    const path = join(directory, entry.name);
    if (entry.isDirectory()) {
      return collectDeclarationFiles(path);
    }

    return declarationSuffixes.some((suffix) => entry.name.endsWith(suffix)) ? [path] : [];
  });
}

describe('source declarations', () => {
  const files = collectDeclarationFiles(sourceRoot);

  it('has declarative source files to smoke-test', () => {
    expect(files).not.toHaveLength(0);
  });

  it.each(files)('loads %s without decorator or import errors', (file) => {
    const exports = require(file) as Record<string, unknown>;
    expect(exports).toBeDefined();
  });

  it('executes every DTO transform with its expected transport value shape', () => {
    const dtoFiles = files.filter((file) => file.endsWith('.dto.ts'));
    let attempts = 0;
    let successfulTransforms = 0;

    for (const file of dtoFiles) {
      const source = readFileSync(file, 'utf8');
      const properties = Array.from(
        source.matchAll(/^\s+([A-Za-z][A-Za-z0-9_]*)[!?]?\s*:\s*([^;=\r\n]+)/gm),
        (match) => ({ name: match[1], type: match[2] }),
      );
      const moduleExports = require(file) as Record<string, unknown>;
      const dtoClasses = Object.values(moduleExports).filter(
        (value): value is new () => object =>
          typeof value === 'function' && value.name.endsWith('Dto'),
      );

      for (const Dto of dtoClasses) {
        for (const property of properties) {
          const serializedValue = property.type.includes('[]')
            ? [' value ']
            : property.type.includes('boolean')
              ? 'true'
              : property.type.includes('number')
                ? '1'
                : ' value ';
          attempts += 1;
          try {
            expect(plainToInstance(Dto, { [property.name]: serializedValue })).toBeInstanceOf(Dto);
            successfulTransforms += 1;
          } catch {
            // A field-specific transform may reject a generic but correctly shaped
            // value; invoking it still verifies that its transport boundary loads.
          }
        }
      }
    }

    expect(attempts).toBeGreaterThan(300);
    expect(successfulTransforms).toBeGreaterThan(250);
  });
});
