/**
 * Hinkal SDK Metro config helper for React Native consumers.
 *
 * Usage in your app's `metro.config.js`:
 *
 *   const { getDefaultConfig } = require("expo/metro-config");
 *   const { applyHinkalMetroConfig } = require("@hinkal/sdk/react-native/metro-config");
 *
 *   const config = getDefaultConfig(__dirname);
 *   module.exports = applyHinkalMetroConfig(config);
 */
const path = require('path');

const shimDir = path.join(__dirname, 'shims');
const shim = (name) => ({
  filePath: path.join(shimDir, `${name}.js`),
  type: 'sourceFile',
});

const isFromHinkalSdk = (originPath) => !!originPath && originPath.includes('/@hinkal/sdk/');

const applyHinkalMetroConfig = (config) => {
  if (!config || !config.resolver) {
    throw new Error('applyHinkalMetroConfig: pass a Metro config (e.g. from `getDefaultConfig(__dirname)`).');
  }

  config.resolver.unstable_enablePackageExports = true;
  config.resolver.unstable_conditionNames = ['require', 'default', 'react-native'];
  config.resolver.sourceExts = Array.from(new Set([...(config.resolver.sourceExts ?? []), 'mjs', 'cjs']));

  config.resolver.extraNodeModules = {
    ...(config.resolver.extraNodeModules ?? {}),
    path: path.join(shimDir, 'path.js'),
    os: path.join(shimDir, 'os.js'),
    fs: path.join(shimDir, 'fs.js'),
    crypto: path.join(shimDir, 'crypto.js'),
    worker_threads: path.join(shimDir, 'worker_threads.js'),
    readline: path.join(shimDir, 'readline.js'),
    constants: path.join(shimDir, 'constants.js'),
    'web-worker': path.join(shimDir, 'web-worker.js'),
    url: path.join(shimDir, 'url.js'),
    assert: require.resolve('assert/'),
  };

  const prevGetTransformOptions = config.transformer && config.transformer.getTransformOptions;
  config.transformer = {
    ...(config.transformer || {}),
    getTransformOptions: async (...args) => {
      const upstream = prevGetTransformOptions ? await prevGetTransformOptions(...args) : {};
      return {
        ...upstream,
        transform: {
          ...(upstream.transform || {}),
          experimentalImportSupport: false,
          inlineRequires: true,
        },
      };
    },
  };

  const upstreamResolve = config.resolver.resolveRequest;

  config.resolver.resolveRequest = (context, moduleName, platform) => {
    console.log('resolveRequest', moduleName);
    if (moduleName === 'url') return shim('url');
    if (moduleName === 'web-worker' || moduleName.startsWith('web-worker/')) return shim('web-worker');
    if (moduleName === 'ffjavascript') return shim('ffjavascript');
    if (moduleName === 'circomlibjs-hinkal-fork') return shim('circomlibjs-hinkal-fork');
    if (moduleName === 'assert') return { filePath: require.resolve('assert/'), type: 'sourceFile' };

    if (isFromHinkalSdk(context.originModulePath)) {
      if (moduleName === 'multiformats' || moduleName.startsWith('multiformats/')) return shim('multiformats');
      if (moduleName === 'snarkjs') return shim('snarkjs');
      if (moduleName === 'idb-keyval') return shim('idb-keyval');
    }

    if (moduleName.startsWith('node:')) {
      const stripped = moduleName.slice(5);
      if (upstreamResolve) return upstreamResolve(context, stripped, platform);
      return context.resolveRequest(context, stripped, platform);
    }

    if (upstreamResolve) return upstreamResolve(context, moduleName, platform);
    return context.resolveRequest(context, moduleName, platform);
  };

  return config;
};

module.exports = { applyHinkalMetroConfig };
