import { readFileSync, readdirSync, statSync } from 'node:fs';
import { join, relative } from 'node:path';

const roots = ['src', 'scripts', 'drizzle'];
const explicitFiles = ['drizzle.config.ts'];
const extensions = new Set([
  '.ts',
  '.tsx',
  '.js',
  '.jsx',
  '.mjs',
  '.cjs',
  '.json',
  '.sql'
]);

const suspiciousChars = [
  '\u0081',
  '\u0087',
  '\u008d',
  '\u0099',
  '\u009e',
  '\u00a0',
  '\u201d',
  '\u20ac'
];

function walk(dir) {
  const entries = readdirSync(dir, { withFileTypes: true });
  const files = [];

  for (const entry of entries) {
    const fullPath = join(dir, entry.name);

    if (entry.isDirectory()) {
      files.push(...walk(fullPath));
      continue;
    }

    if ([...extensions].some((extension) => entry.name.endsWith(extension))) {
      files.push(fullPath);
    }
  }

  return files;
}

function hasUtf8Bom(buffer) {
  return buffer.length >= 3 && buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf;
}

function findSuspiciousChars(text) {
  return suspiciousChars.filter((char) => text.includes(char));
}

const filesToCheck = [
  ...roots.flatMap((root) => (statSync(root, { throwIfNoEntry: false })?.isDirectory() ? walk(root) : [])),
  ...explicitFiles.filter((file) => statSync(file, { throwIfNoEntry: false })?.isFile())
];

const issues = [];

for (const file of filesToCheck) {
  const buffer = readFileSync(file);

  if (hasUtf8Bom(buffer)) {
    issues.push(`${relative(process.cwd(), file)}: UTF-8 BOM detected`);
  }

  let text;

  try {
    text = buffer.toString('utf8');
  } catch (error) {
    issues.push(`${relative(process.cwd(), file)}: unable to decode as UTF-8 (${error.message})`);
    continue;
  }

  const suspicious = findSuspiciousChars(text);

  if (suspicious.length > 0) {
    const labels = suspicious.map((char) => `U+${char.codePointAt(0).toString(16).toUpperCase().padStart(4, '0')}`);
    issues.push(`${relative(process.cwd(), file)}: suspicious mojibake markers found (${labels.join(', ')})`);
  }
}

if (issues.length > 0) {
  console.error('Encoding verification failed:');
  for (const issue of issues) {
    console.error(`- ${issue}`);
  }
  process.exit(1);
}

console.log(`Encoding verification passed for ${filesToCheck.length} files.`);
