import { build } from 'esbuild';
import path from 'path';

const cfDir = __dirname;

async function main() {
  const result = await build({
    entryPoints: [path.resolve(cfDir, 'worker.ts')],
    bundle: true,
    format: 'esm',
    platform: 'node', // Use node platform — CF Workers nodejs_compat handles Node built-ins
    target: 'esnext',
    outdir: path.resolve(cfDir, 'dist'),
    minify: true,
    sourcemap: true,
    metafile: true,
    mainFields: ['module', 'main'], // Resolve ESM first, then CJS
    conditions: ['import', 'module', 'default'],
    // CF Workers runtime provides these
    external: [
      'cloudflare:*',
      '__STATIC_CONTENT_MANIFEST',
    ],
    alias: {
      // === Sequelize → D1 shim ===
      'sequelize': path.resolve(cfDir, 'shims/sequelize-d1/index.ts'),
      'sqlite3': path.resolve(cfDir, 'shims/noop.ts'),
      'cls-hooked': path.resolve(cfDir, 'shims/noop.ts'),
      'express-async-errors': path.resolve(cfDir, 'shims/noop.ts'),

      // === Express → shim ===
      'express': path.resolve(cfDir, 'shims/express-compat/index.ts'),
      'cors': path.resolve(cfDir, 'shims/cors.ts'),
      'cookie-parser': path.resolve(cfDir, 'shims/cookie-parser.ts'),

      // === @blocklet/sdk — each sub-path needs its own alias ===
      // NOTE: more specific paths MUST come before less specific ones
      '@blocklet/sdk/lib/middlewares/fallback': path.resolve(cfDir, 'shims/blocklet-sdk/fallback.ts'),
      '@blocklet/sdk/lib/middlewares/cdn': path.resolve(cfDir, 'shims/blocklet-sdk/cdn.ts'),
      '@blocklet/sdk/lib/middlewares/session': path.resolve(cfDir, 'shims/blocklet-sdk/session.ts'),
      '@blocklet/sdk/lib/middlewares': path.resolve(cfDir, 'shims/blocklet-sdk/middlewares.ts'),
      '@blocklet/sdk/lib/env': path.resolve(cfDir, 'shims/blocklet-sdk/env.ts'),
      '@blocklet/sdk/lib/config': path.resolve(cfDir, 'shims/blocklet-sdk/config.ts'),
      '@blocklet/sdk/lib/component': path.resolve(cfDir, 'shims/blocklet-sdk/component.ts'),
      '@blocklet/sdk/lib/wallet': path.resolve(cfDir, 'shims/blocklet-sdk/wallet.ts'),
      '@blocklet/sdk/lib/wallet-authenticator': path.resolve(cfDir, 'shims/blocklet-sdk/wallet-authenticator.ts'),
      '@blocklet/sdk/lib/wallet-handler': path.resolve(cfDir, 'shims/blocklet-sdk/wallet-handler.ts'),
      '@blocklet/sdk/lib/security': path.resolve(cfDir, 'shims/blocklet-sdk/security.ts'),
      '@blocklet/sdk/lib/util/verify-sign': path.resolve(cfDir, 'shims/blocklet-sdk/verify-sign.ts'),
      '@blocklet/sdk/lib/util/component-api': path.resolve(cfDir, 'shims/blocklet-sdk/component-api.ts'),
      '@blocklet/sdk/lib/error-handler': path.resolve(cfDir, 'shims/noop.ts'),
      '@blocklet/sdk/lib/did': path.resolve(cfDir, 'shims/blocklet-sdk/did.ts'),
      '@blocklet/sdk/lib/types/notification': path.resolve(cfDir, 'shims/noop.ts'),
      '@blocklet/sdk/service/notification': path.resolve(cfDir, 'shims/blocklet-sdk/notification.ts'),
      '@blocklet/sdk/service/eventbus': path.resolve(cfDir, 'shims/blocklet-sdk/eventbus.ts'),
      '@blocklet/sdk/service/auth': path.resolve(cfDir, 'shims/blocklet-sdk/auth-service.ts'),
      '@blocklet/sdk/service/blocklet': path.resolve(cfDir, 'shims/blocklet-sdk/auth-service.ts'),
      // Catch-all for any other @blocklet/sdk paths
      '@blocklet/sdk': path.resolve(cfDir, 'shims/blocklet-sdk/index.ts'),

      // === Other @blocklet packages ===
      '@blocklet/xss': path.resolve(cfDir, 'shims/xss.ts'),
      '@blocklet/error': path.resolve(cfDir, 'shims/error.ts'),
      '@blocklet/logger': path.resolve(cfDir, 'shims/blocklet-sdk/logger.ts'),
      '@blocklet/did-space-js': path.resolve(cfDir, 'shims/did-space-js.ts'),
      '@blocklet/payment-vendor': path.resolve(cfDir, 'shims/payment-vendor.ts'),

      // === ArcBlock / DID packages ===
      '@arcblock/did-connect-storage-nedb': path.resolve(cfDir, 'shims/nedb-storage.ts'),

      // === Other dependencies ===
      'ws': path.resolve(cfDir, 'shims/ws-lite.ts'),
      'pg': path.resolve(cfDir, 'shims/noop.ts'),
      'follow-redirects': path.resolve(cfDir, 'shims/noop.ts'),
      'crypto-js': path.resolve(cfDir, 'shims/crypto-js-warn.ts'),
      'mime-types': path.resolve(cfDir, 'shims/mime-types.ts'),
      '@abtnode/cron': path.resolve(cfDir, 'shims/cron.ts'),
      'querystring': path.resolve(cfDir, 'shims/querystring.ts'),
      'dotenv-flow': path.resolve(cfDir, 'shims/noop.ts'),
      'fastq': path.resolve(cfDir, 'shims/fastq.ts'),
    },
    banner: {
      js: [
        // process polyfills
        'if (typeof process !== "undefined") { if (!process.stderr) process.stderr = { fd: 2, write: function(){} }; else if (process.stderr.fd === undefined) process.stderr.fd = 2; if (!process.stdout) process.stdout = { fd: 1, write: function(){} }; else if (process.stdout.fd === undefined) process.stdout.fd = 1; }',
        'if (typeof process !== "undefined" && !process.on) { process.on = function(){}; process.once = function(){}; process.off = function(){}; process.removeListener = function(){}; process.emit = function(){}; process.exit = function(){}; }',
        // CF Workers: defer timers called during global scope init until first request
        'var __deferredTimers = []; var __timersReady = false;',
        'globalThis.__flushDeferredTimers = function() { if (__timersReady) return; __timersReady = true; var q = __deferredTimers; __deferredTimers = []; q.forEach(function(entry) { try { entry.fn.apply(null, entry.args); } catch(e) { console.error("deferred timer error:", e); } }); };',
        'var _origSetTimeout = globalThis.setTimeout; globalThis.setTimeout = function() { if (!__timersReady) { var args = arguments; __deferredTimers.push({fn: function(){ _origSetTimeout.apply(globalThis, args); }, args: []}); return { unref: function(){}, ref: function(){}, [Symbol.toPrimitive]: function(){ return 0; } }; } var id = _origSetTimeout.apply(this, arguments); if (typeof id === "number") { return { unref: function(){}, ref: function(){}, [Symbol.toPrimitive]: function(){ return id; } }; } if (id && !id.unref) id.unref = function(){}; return id; };',
        'var _origSetInterval = globalThis.setInterval; globalThis.setInterval = function() { if (!__timersReady) { var args = arguments; __deferredTimers.push({fn: function(){ _origSetInterval.apply(globalThis, args); }, args: []}); return { unref: function(){}, ref: function(){}, [Symbol.toPrimitive]: function(){ return 0; } }; } var id = _origSetInterval.apply(this, arguments); if (typeof id === "number") { return { unref: function(){}, ref: function(){}, [Symbol.toPrimitive]: function(){ return id; } }; } if (id && !id.unref) id.unref = function(){}; return id; };',
        'var _origSetImmediate = globalThis.setImmediate; if (_origSetImmediate) { globalThis.setImmediate = function() { if (!__timersReady) { var args = arguments; __deferredTimers.push({fn: function(){ _origSetImmediate.apply(globalThis, args); }, args: []}); return { unref: function(){}, ref: function(){} }; } return _origSetImmediate.apply(this, arguments); }; } else { globalThis.setImmediate = function(fn) { if (!__timersReady) { __deferredTimers.push({fn: function(){ _origSetTimeout(fn, 0); }, args: []}); return { unref: function(){}, ref: function(){} }; } return _origSetTimeout(fn, 0); }; }',
        'if (typeof process !== "undefined") { var _origNextTick = process.nextTick; if (_origNextTick) { process.nextTick = function() { if (!__timersReady) { var args = Array.prototype.slice.call(arguments); var fn = args.shift(); __deferredTimers.push({fn: function(){ _origNextTick.apply(process, [fn].concat(args)); }, args: []}); return; } return _origNextTick.apply(process, arguments); }; } else { process.nextTick = function(fn) { if (!__timersReady) { __deferredTimers.push({fn: function(){ _origSetTimeout(fn, 0); }, args: []}); return; } _origSetTimeout(fn, 0); }; } }',
        // require polyfill for Node built-ins
        '(function() {',
        '  var _origRequire = typeof globalThis.require === "function" ? globalThis.require : null;',
        '  var _qsShim = { stringify: function(obj) { return new URLSearchParams(obj).toString(); }, parse: function(str) { return Object.fromEntries(new URLSearchParams(str).entries()); } };',
        '  var _utilShim = { deprecate: function(fn) { return fn; }, inspect: function(obj) { return JSON.stringify(obj); }, inherits: function(ctor, superCtor) { if (superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor } }); } }, format: function() { return Array.prototype.join.call(arguments, " "); } };',
        '  var _ttyShim = { isatty: function() { return false; }, ReadStream: function(){}, WriteStream: function(){} };',
        '  var _emptyMod = {};',
        '  var _httpShim = (function() { var _noop = function(){}; _noop.prototype = {}; return { request: _noop, get: _noop, Agent: _noop, globalAgent: { options: {} }, STATUS_CODES: {}, METHODS: [], createServer: _noop }; })();',
        '  globalThis.require = function(mod) {',
        '    if (mod === "querystring") return _qsShim;',
        '    if (mod === "util") return _utilShim;',
        '    if (mod === "tty") return _ttyShim;',
        '    if (mod === "http" || mod === "https") return _httpShim;',
        '    if (mod === "crypto") return { randomBytes: function(n) { var b = new Uint8Array(n); crypto.getRandomValues(b); return Buffer.from(b); }, createHash: function() { return { update: function() { return this; }, digest: function() { return ""; } }; } };',
        '    if (mod === "stream" || mod === "fs" || mod === "net" || mod === "os" || mod === "path" || mod === "zlib" || mod === "tls" || mod === "child_process" || mod === "worker_threads") return _emptyMod;',
        '    if (_origRequire) return _origRequire(mod);',
        '    console.warn("Cannot require: " + mod);',
        '    return _emptyMod;',
        '  };',
        '})();',
      ].join('\n'),
    },
    plugins: [
      {
        name: 'rolldown-runtime-shim',
        setup(b: any) {
          b.onResolve({ filter: /rolldown_runtime/ }, () => ({
            path: path.resolve(cfDir, 'shims/rolldown-runtime.ts'),
          }));
        },
      },
    ],
    logLevel: 'warning',
  });

  const outputs = Object.keys(result.metafile!.outputs);
  for (const o of outputs) {
    const size = result.metafile!.outputs[o].bytes;
    if (!o.endsWith('.map')) {
      console.log(`${o}: ${(size / 1024).toFixed(1)}KB`);
    }
  }
  console.log('BUILD SUCCESS');
}

main().catch((err) => {
  console.error('BUILD FAILED');
  console.error(err.message);
  if (err.errors) {
    for (const e of err.errors.slice(0, 30)) {
      console.error(`  ${e.text}`);
      if (e.location) {
        console.error(`    at ${e.location.file}:${e.location.line}`);
      }
    }
  }
  process.exit(1);
});
