const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');
const { reportErrorToRemote } = require('./report-error-to-remote');

const VIRTUAL_ROOT = path.join(__dirname, '../.metro-virtual');
const VIRTUAL_ROOT_UNRESOLVED = path.join(VIRTUAL_ROOT, 'unresolved');

const handleResolveRequestError = ({
  error,
  context,
  moduleName,
  platform,
}) => {
  const errorMessage = `Unable to resolve module '${moduleName}' from '${context.originModulePath}'`;
  const syntheticError = new Error(errorMessage);
  syntheticError.stack = error.stack;
  reportErrorToRemote({ error: syntheticError }).catch((_reportError) => {
    // no-op
  });
  if (process.env.NODE_ENV === 'production') throw error;
  if (platform === 'android') throw error;
  if (!__DEV__ && process.env.EXPO_PUBLIC_CREATE_ENV !== 'DEVELOPMENT')
    throw error;

  // Build a deterministic virtual file path for this failed request
  const key = `${moduleName}|${context.originModulePath}|${platform}`;
  const hash = crypto
    .createHash('sha256')
    .update(key)
    .digest('hex')
    .slice(0, 16);

  fs.mkdirSync(VIRTUAL_ROOT_UNRESOLVED, { recursive: true });
  const vfile = path.join(VIRTUAL_ROOT_UNRESOLVED, `throw-${hash}.js`);

  // Serialize a safe payload for the client
  const payload = {
    moduleName,
    from: context.originModulePath,
    platform,
    originalMessage: String(
      error?.message ? error.message : 'Unknown resolve error'
    ),
  };

  const code = [
    '// Auto generated by custom Metro resolver',
    '(function(){',
    `  var info = ${JSON.stringify(payload)};`,
    "  var msg = 'Unable to resolve \"' + info.moduleName + '\" from \"' + info.from + '\"';",
    "  msg += '\\n\\n' + info.originalMessage;",
    '  var e = new Error(msg);',
    "  e.name = 'ModuleResolveError';",
    "  e.code = 'MODULE_RESOLVE_FAILED';",
    '  throw e;',
    '})();',
    'export {};', // keep ESM shape harmlessly
    '',
  ].join('\n');

  // Only write if content changed — avoids bumping mtime and triggering Metro rebuild loop
  const existingContent = fs.existsSync(vfile) ? fs.readFileSync(vfile, 'utf8') : null;
  if (existingContent !== code) {
    fs.writeFileSync(vfile, code, 'utf8');
  }

  // Tell Metro to load our thrower as a real source file
  return {
    filePath: vfile,
    type: 'sourceFile',
  };
};

module.exports = {
  handleResolveRequestError,
  VIRTUAL_ROOT,
  VIRTUAL_ROOT_UNRESOLVED,
};
