const { spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const repoRoot = path.resolve(__dirname, '..');
const prismaSchemaPath = path.join(repoRoot, 'prisma', 'schema.prisma');
const generatedSchemaPath = path.join(
  repoRoot,
  'node_modules',
  '.prisma',
  'client',
  'schema.prisma'
);

function readFileIfExists(filePath) {
  try {
    return fs.readFileSync(filePath, 'utf8');
  } catch (error) {
    if (error && error.code === 'ENOENT') {
      return null;
    }

    throw error;
  }
}

function normalizeSchema(schema) {
  return schema.replace(/\s+/g, '');
}

function schemasMatch() {
  const sourceSchema = readFileIfExists(prismaSchemaPath);
  const generatedSchema = readFileIfExists(generatedSchemaPath);

  return Boolean(
    sourceSchema &&
      generatedSchema &&
      normalizeSchema(sourceSchema) === normalizeSchema(generatedSchema)
  );
}

function run(command, args, options = {}) {
  const shouldUseShell =
    process.platform === 'win32' && command.toLowerCase().endsWith('.cmd');

  const result = spawnSync(command, args, {
    cwd: repoRoot,
    encoding: 'utf8',
    stdio: 'pipe',
    shell: shouldUseShell,
    env: {
      ...process.env,
      ...options.env,
    },
  });

  if (result.stdout) {
    process.stdout.write(result.stdout);
  }

  if (result.stderr) {
    process.stderr.write(result.stderr);
  }

  return result;
}

function isWindowsPrismaRenameLock(output) {
  const text = [output.stdout, output.stderr]
    .filter(Boolean)
    .join('\n');

  return (
    process.platform === 'win32' &&
    text.includes('EPERM: operation not permitted, rename') &&
    text.includes('query_engine-windows.dll.node')
  );
}

function runPrismaGenerate() {
  const prismaBin =
    process.platform === 'win32'
      ? path.join(repoRoot, 'node_modules', '.bin', 'prisma.cmd')
      : path.join(repoRoot, 'node_modules', '.bin', 'prisma');

  const result = run(prismaBin, ['generate']);

  if (result.error) {
    throw result.error;
  }

  if ((result.status ?? 1) === 0) {
    return 0;
  }

  if (!isWindowsPrismaRenameLock(result) || !schemasMatch()) {
    return result.status ?? 1;
  }

  console.warn(
    '\nPrisma generate hit a Windows file lock, but the generated client already matches prisma/schema.prisma. Continuing with the existing client.\n'
  );

  return 0;
}

function runNextBuild() {
  const nextBin =
    process.platform === 'win32'
      ? path.join(repoRoot, 'node_modules', '.bin', 'next.cmd')
      : path.join(repoRoot, 'node_modules', '.bin', 'next');

  // WSL needs more aggressive memory settings
  const isWSL = process.platform === 'linux' && require('fs').existsSync('/proc/version') &&
    require('fs').readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');

  const memoryLimit = isWSL ? '8192' : '4096';

  return run(nextBin, ['build'], {
    env: {
      NODE_OPTIONS: `--max-old-space-size=${memoryLimit}`,
      SKIP_ENV_VALIDATION: 'true',
    },
  });
}

const prismaExitCode = runPrismaGenerate();
if (prismaExitCode !== 0) {
  process.exit(prismaExitCode);
}

const nextResult = runNextBuild();
if (nextResult.error) {
  throw nextResult.error;
}

process.exit(nextResult.status ?? 1);
