// 浏览器端 Node.js 内置模块 polyfill
// 用于加载 webpack/ncc 打包的官方音源插件
// 提供 http/https (基于 fetch)、stream、util、events、crypto、url、zlib、path、fs、buffer 等

function createEventEmitter(): any {
  const listeners: Record<string, Function[]> = {};
  const self: any = {
    on(event: string, fn: Function) {
      (listeners[event] = listeners[event] || []).push(fn);
      return self;
    },
    once(event: string, fn: Function) {
      const wrapper = (...args: any[]) => {
        self.off(event, wrapper);
        fn(...args);
      };
      return self.on(event, wrapper);
    },
    off(event: string, fn: Function) {
      if (listeners[event]) {
        listeners[event] = listeners[event].filter((f: Function) => f !== fn);
      }
      return self;
    },
    emit(event: string, ...args: any[]) {
      const fns = listeners[event] || [];
      for (const fn of fns) {
        try { fn.apply(null, args); } catch (_) {}
      }
      return true;
    },
    addListener: function() { return self.on.apply(self, arguments as any); },
    removeListener: function() { return self.off.apply(self, arguments as any); },
    removeAllListeners(event?: string) {
      if (event) delete listeners[event];
      else { for (const k in listeners) delete listeners[k]; }
      return self;
    },
    listeners(event: string) { return listeners[event] || []; },
  };
  return self;
}

function createStreamBase(): any {
  function Stream() {}
  Stream.prototype.pipe = function(dest: any) { return dest; };
  Stream.prototype.on = function(_e: string, _fn: Function) { return this; };
  Stream.prototype.emit = function() { return true; };
  return Stream;
}

function createReadable(): any {
  function Readable() {}
  Readable.prototype.pipe = function(dest: any) { return dest; };
  Readable.prototype.on = function() { return this; };
  Readable.prototype.once = function() { return this; };
  Readable.prototype.emit = function() { return true; };
  Readable.prototype.read = function() { return null; };
  Readable.prototype.pause = function() { return this; };
  Readable.prototype.resume = function() { return this; };
  Readable.prototype.push = function() { return this; };
  return Readable;
}

function createWritable(): any {
  function Writable() {}
  Writable.prototype.write = function() { return true; };
  Writable.prototype.end = function() { return this; };
  Writable.prototype.on = function() { return this; };
  Writable.prototype.once = function() { return this; };
  Writable.prototype.emit = function() { return true; };
  return Writable;
}

function createTransform(): any {
  function Transform() {}
  Transform.prototype.pipe = function(dest: any) { return dest; };
  Transform.prototype.write = function() { return true; };
  Transform.prototype.end = function() { return this; };
  Transform.prototype.on = function() { return this; };
  Transform.prototype.once = function() { return this; };
  Transform.prototype.emit = function() { return true; };
  return Transform;
}

function createPassThrough(): any {
  function PassThrough() {}
  PassThrough.prototype.pipe = function(dest: any) { return dest; };
  PassThrough.prototype.write = function() { return true; };
  PassThrough.prototype.end = function() { return this; };
  PassThrough.prototype.on = function() { return this; };
  PassThrough.prototype.once = function() { return this; };
  PassThrough.prototype.emit = function() { return true; };
  return PassThrough;
}

function parseUrl(u: string): any {
  try {
    const url = new URL(u, typeof window !== 'undefined' ? window.location.href : 'http://localhost/');
    return {
      protocol: url.protocol,
      hostname: url.hostname,
      host: url.host,
      port: url.port || (url.protocol === 'https:' ? '443' : '80'),
      pathname: url.pathname,
      path: url.pathname + url.search,
      search: url.search,
      query: Object.fromEntries(url.searchParams.entries()),
      hash: url.hash,
      href: url.href,
      auth: url.username ? url.username + (url.password ? ':' + url.password : '') : '',
    };
  } catch {
    return { protocol: '', hostname: '', host: '', pathname: '', path: '', href: u, port: '80' };
  }
}

// http/https 模块 - 用 fetch 实现
function createHttpModule(isHttps: boolean): any {
  function request(options: any, callback?: (res: any) => void): any {
    let url: string;
    let opts: any = typeof options === 'string' ? {} : { ...options };
    if (typeof options === 'string') {
      url = options;
    } else if (options && typeof options === 'object') {
      const protocol = opts.protocol || (isHttps ? 'https:' : 'http:');
      const host = opts.hostname || opts.host || 'localhost';
      const port = opts.port ? ':' + opts.port : '';
      const path = opts.path || opts.pathname || '/';
      url = protocol + '//' + host + port + path;
    } else {
      url = String(options);
    }

    const method = opts.method || 'GET';
    const headers = opts.headers || {};

    const req: any = createEventEmitter();
    let requestBodyChunks: Uint8Array[] = [];
    let ended = false;

    req.write = function(chunk: any) {
      if (chunk) requestBodyChunks.push(typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk);
      return true;
    };
    req.end = function(chunk: any) {
      if (chunk) req.write(chunk);
      if (ended) return;
      ended = true;

      let body: any = undefined;
      if (method !== 'GET' && method !== 'HEAD' && requestBodyChunks.length > 0) {
        if (requestBodyChunks.length === 1) body = requestBodyChunks[0];
        else {
          const total = requestBodyChunks.reduce((a, b) => a + b.length, 0);
          const merged = new Uint8Array(total);
          let offset = 0;
          for (const c of requestBodyChunks) { merged.set(c, offset); offset += c.length; }
          body = merged;
        }
      }

      fetch(url, {
        method,
        headers,
        body,
        signal: opts.signal,
        mode: 'cors',
        credentials: 'omit',
      } as any).then((resp: any) => {
        const res: any = createEventEmitter();
        res.statusCode = resp.status;
        res.statusMessage = resp.statusText;
        res.headers = {};
        try {
          resp.headers.forEach((v: string, k: string) => { res.headers[k.toLowerCase()] = v; });
        } catch {}
        res.setEncoding = function() { return res; };
        res.pipe = function(dest: any) {
          resp.arrayBuffer().then((buf: ArrayBuffer) => {
            if (dest && typeof dest.write === 'function') dest.write(new Uint8Array(buf));
            if (dest && typeof dest.end === 'function') dest.end();
            res.emit('end');
          }).catch((err: Error) => res.emit('error', err));
          return dest;
        };
        res.text = () => resp.text();
        res.json = () => resp.json();
        res.body = resp.body;
        if (callback) callback(res);
        req.emit('response', res);

        // 如果没有 callback，等待 body 然后发射 data/end 事件
        if (!callback) {
          resp.arrayBuffer().then((buf: ArrayBuffer) => {
            res.emit('data', new Uint8Array(buf));
            res.emit('end');
          }).catch((err: Error) => res.emit('error', err));
        }
      }).catch((err: Error) => {
        req.emit('error', err);
      });
      return req;
    };
    req.abort = function() { req.emit('abort'); };
    req.setTimeout = function() { return req; };
    req.setHeader = function(name: string, value: string) { (headers as any)[name.toLowerCase()] = value; return req; };
    req.getHeader = function(name: string) { return (headers as any)[name.toLowerCase()]; };
    req.removeHeader = function(name: string) { delete (headers as any)[name.toLowerCase()]; };

    // 如果用户立即调用 req.end()，我们需要在 next tick 开始 - 但实际上用户必须自己调用 end
    return req;
  }

  function get(options: any, callback?: (res: any) => void): any {
    const req = request(options, callback);
    req.end();
    return req;
  }

  return {
    request,
    get,
    createServer: () => ({ listen: () => {}, close: () => {} }),
    Agent: function() { this.maxSockets = 50; },
    globalAgent: { maxSockets: 50 },
    STATUS_CODES: {},
  };
}

// util 模块
const utilModule: any = {
  inspect: (obj: any) => {
    try { return JSON.stringify(obj); } catch { return String(obj); }
  },
  format: function() { return Array.prototype.slice.call(arguments).join(' '); },
  inherits: function(ctor: any, superCtor: any) {
    if (!superCtor || !superCtor.prototype) return;
    ctor.super_ = superCtor;
    const origProto = ctor.prototype;
    ctor.prototype = Object.create(superCtor.prototype, {
      constructor: { value: ctor, enumerable: false, writable: true, configurable: true },
    });
    // 保留原型上原有的属性
    if (origProto) {
      for (const k in origProto) {
        if (Object.prototype.hasOwnProperty.call(origProto, k)) {
          ctor.prototype[k] = origProto[k];
        }
      }
    }
  },
  promisify: function(fn: any) {
    return function() {
      const args = Array.prototype.slice.call(arguments);
      return new Promise((resolve, reject) => {
        args.push((err: any, ...rest: any[]) => {
          if (err) reject(err);
          else resolve(rest.length === 1 ? rest[0] : rest);
        });
        try { fn.apply(null, args); } catch (e) { reject(e); }
      });
    };
  },
  callbackify: function(fn: any) {
    return function() {
      const args = Array.prototype.slice.call(arguments);
      const cb = args.pop();
      fn.apply(null, args).then((r: any) => cb(null, r)).catch((e: Error) => cb(e));
    };
  },
  TextDecoder: typeof (globalThis as any).TextDecoder !== 'undefined' ? (globalThis as any).TextDecoder : function(this: any) { this.decode = function() { return ''; }; },
  TextEncoder: typeof (globalThis as any).TextEncoder !== 'undefined' ? (globalThis as any).TextEncoder : function(this: any) { this.encode = function(s: string) { return s; }; },
  types: { isDate: (x: any) => x instanceof Date, isRegExp: (x: any) => x instanceof RegExp, isError: (x: any) => x instanceof Error, isArray: Array.isArray, isFunction: (x: any) => typeof x === 'function' },
};

// crypto 模块
const cryptoModule: any = {
  createHash: function(algo: string) {
    const chunks: string[] = [];
    return {
      update: function(chunk: any) { chunks.push(String(chunk)); return this; },
      digest: function(encoding?: string) {
        // 简化：不做真正的哈希，返回一个稳定的伪随机值
        const s = algo + ':' + chunks.join('');
        let h = 0;
        for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0;
        const hex = Math.abs(h).toString(16).padStart(16, '0') + Math.abs(h * 31).toString(16).padStart(16, '0');
        if (encoding === 'hex') return hex;
        if (encoding === 'base64') return btoa(hex);
        return hex;
      },
    };
  },
  createHmac: function(algo: string, key: any) {
    const chunks: string[] = [];
    return {
      update: function(chunk: any) { chunks.push(String(chunk)); return this; },
      digest: function(encoding?: string) {
        const s = 'hmac:' + algo + ':' + key + ':' + chunks.join('');
        let h = 0;
        for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0;
        const hex = Math.abs(h).toString(16).padStart(16, '0') + Math.abs(h * 17).toString(16).padStart(16, '0');
        if (encoding === 'hex') return hex;
        if (encoding === 'base64') return btoa(hex);
        return hex;
      },
    };
  },
  randomBytes: function(n: number, cb?: (err: Error | null, buf: any) => void) {
    const arr = new Uint8Array(n);
    if (typeof (globalThis as any).crypto !== 'undefined' && (globalThis as any).crypto.getRandomValues) {
      (globalThis as any).crypto.getRandomValues(arr);
    } else {
      for (let i = 0; i < n; i++) arr[i] = Math.floor(Math.random() * 256);
    }
    const buf: any = {
      buffer: arr,
      length: n,
      toString: function(enc?: string) {
        if (enc === 'hex') {
          let s = '';
          for (let i = 0; i < n; i++) s += arr[i].toString(16).padStart(2, '0');
          return s;
        }
        if (enc === 'base64') {
          let s = '';
          for (let i = 0; i < n; i++) s += String.fromCharCode(arr[i]);
          return btoa(s);
        }
        return String.fromCharCode.apply(null, arr as any);
      },
    };
    if (cb) { setTimeout(() => cb(null, buf), 0); return undefined; }
    return buf;
  },
  randomUUID: function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      const r = Math.random() * 16 | 0;
      const v = c === 'x' ? r : (r & 0x3 | 0x8);
      return v.toString(16);
    });
  },
};

// 完整的 polyfill 注册表
export function createNodePolyfills(): Record<string, any> {
  const streamMod: any = {
    Stream: createStreamBase(),
    Readable: createReadable(),
    Writable: createWritable(),
    Duplex: createTransform(),
    Transform: createTransform(),
    PassThrough: createPassThrough(),
    pipeline: (_a: any, b: any, cb?: () => void) => { if (cb) cb(); return b; },
    finished: (_s: any, cb: () => void) => { cb(); },
  };
  streamMod.Stream.prototype = Object.create(createEventEmitter());
  streamMod.Readable.prototype = Object.create(streamMod.Stream.prototype);
  streamMod.Writable.prototype = Object.create(streamMod.Stream.prototype);
  streamMod.Duplex.prototype = Object.create(streamMod.Stream.prototype);
  streamMod.Transform.prototype = Object.create(streamMod.Duplex.prototype);
  streamMod.PassThrough.prototype = Object.create(streamMod.Transform.prototype);

  const bufferModule: any = {
    from: (x: any) => {
      if (x instanceof Uint8Array) return x;
      if (typeof x === 'string') return new TextEncoder().encode(x);
      if (Array.isArray(x)) return new Uint8Array(x);
      return new Uint8Array(x || 0);
    },
    alloc: (n: number) => new Uint8Array(n),
    allocUnsafe: (n: number) => new Uint8Array(n),
    isBuffer: () => false,
    concat: (arrs: any[]) => {
      const total = arrs.reduce((a, b) => a + (b?.length || 0), 0);
      const merged = new Uint8Array(total);
      let offset = 0;
      for (const c of arrs) { if (c) { merged.set(c, offset); offset += c.length; } }
      return merged;
    },
    byteLength: (x: any) => typeof x === 'string' ? new TextEncoder().encode(x).length : (x?.length || 0),
    compare: (_a: any, _b: any) => 0,
    isEncoding: () => true,
  };

  return {
    http: createHttpModule(false),
    https: createHttpModule(true),
    http2: createHttpModule(true),
    stream: streamMod,
    util: utilModule,
    events: { EventEmitter: function() { return createEventEmitter(); } },
    crypto: cryptoModule,
    url: {
      parse: parseUrl,
      format: (o: any) => o.href || `${o.protocol}//${o.host}${o.path || o.pathname || ''}`,
      resolve: (_from: string, to: string) => { try { return new URL(to, _from).href; } catch { return to; } },
      URL: typeof (globalThis as any).URL !== 'undefined' ? (globalThis as any).URL : function() {},
      URLSearchParams: typeof (globalThis as any).URLSearchParams !== 'undefined' ? (globalThis as any).URLSearchParams : function() {},
    },
    zlib: {
      Z_NO_FLUSH: 0,
      Z_PARTIAL_FLUSH: 1,
      Z_SYNC_FLUSH: 2,
      Z_FULL_FLUSH: 3,
      Z_FINISH: 4,
      Z_BLOCK: 5,
      Z_TREES: 6,
      Z_OK: 0,
      Z_STREAM_END: 1,
      Z_NEED_DICT: 2,
      Z_ERRNO: -1,
      Z_STREAM_ERROR: -2,
      Z_DATA_ERROR: -3,
      Z_MEM_ERROR: -4,
      Z_BUF_ERROR: -5,
      Z_VERSION_ERROR: -6,
      Z_NO_COMPRESSION: 0,
      Z_BEST_SPEED: 1,
      Z_BEST_COMPRESSION: 9,
      Z_DEFAULT_COMPRESSION: -1,
      Z_DEFAULT_STRATEGY: 0,
      Z_FILTERED: 1,
      Z_HUFFMAN_ONLY: 2,
      Z_RLE: 3,
      Z_FIXED: 4,
      DEFLATE: 1,
      INFLATE: 2,
      GZIP: 3,
      GUNZIP: 4,
      DEFLATERAW: 5,
      INFLATERAW: 6,
      UNZIP: 7,
      BROTLI_DECODE: 8,
      BROTLI_ENCODE: 9,
      gunzipSync: (d: any) => d,
      inflateSync: (d: any) => d,
      deflateSync: (d: any) => d,
      gzipSync: (d: any) => d,
      gunzip: (d: any, cb: (err: Error | null, out: any) => void) => { setTimeout(() => cb(null, d), 0); },
      inflate: (d: any, cb: (err: Error | null, out: any) => void) => { setTimeout(() => cb(null, d), 0); },
      createGunzip: () => { const p: any = Object.create(streamMod !== undefined ? streamMod.Transform.prototype : {}); p.write = () => true; p.end = function() { this.emit && this.emit('end'); return this; }; return p; },
      createInflate: () => { const p: any = Object.create(streamMod !== undefined ? streamMod.Transform.prototype : {}); p.write = () => true; p.end = function() { this.emit && this.emit('end'); return this; }; return p; },
      createGzip: () => { const p: any = Object.create(streamMod !== undefined ? streamMod.Transform.prototype : {}); p.write = () => true; p.end = function() { this.emit && this.emit('end'); return this; }; return p; },
    },
    path: {
      join: function() { return Array.prototype.slice.call(arguments).filter(Boolean).join('/').replace(/\/+/g, '/'); },
      resolve: function() { return Array.prototype.slice.call(arguments).filter(Boolean).join('/'); },
      dirname: (p: string) => String(p).split('/').slice(0, -1).join('/') || '/',
      basename: (p: string, ext?: string) => { const base = String(p).split('/').pop() || ''; return ext && base.endsWith(ext) ? base.slice(0, -ext.length) : base; },
      extname: (p: string) => { const m = String(p).match(/\.[^.]*$/); return m ? m[0] : ''; },
      sep: '/', delimiter: ':',
      parse: (p: string) => { const parts = String(p).split('/'); return { root: '/', dir: parts.slice(0, -1).join('/'), base: parts[parts.length - 1], ext: '', name: parts[parts.length - 1].replace(/\.[^.]*$/, '') }; },
      normalize: (p: string) => p,
      isAbsolute: (p: string) => p && p[0] === '/',
      relative: (_from: string, to: string) => to,
    },
    fs: {
      readFileSync: () => '',
      writeFileSync: () => {},
      existsSync: () => false,
      createWriteStream: () => { const s = Object.create(streamMod.Writable.prototype); s.write = () => true; s.end = function() { this.emit && this.emit('finish'); return this; }; return s; },
      createReadStream: () => { const s = Object.create(streamMod.Readable.prototype); s.pipe = (d: any) => { s.emit && s.emit('end'); return d; }; return s; },
      readFile: (_path: any, enc: any, cb: (err: Error | null, data: any) => void) => { if (typeof enc === 'function') cb = enc; setTimeout(() => cb(null, ''), 0); },
      writeFile: (_p: any, _d: any, cb?: (err: Error | null) => void) => { if (typeof cb === 'function') setTimeout(() => cb(null), 0); },
      mkdirSync: () => {},
      statSync: () => ({ isDirectory: () => false, isFile: () => true, size: 0, mtime: new Date() }),
      unlinkSync: () => {},
      promises: {
        readFile: async () => '',
        writeFile: async () => {},
        mkdir: async () => {},
      },
    },
    os: {
      platform: () => 'browser',
      type: () => 'Browser',
      release: () => '1.0',
      tmpdir: () => '/tmp',
      homedir: () => '/',
      EOL: '\n',
      arch: () => 'x64',
      cpus: () => [],
      totalmem: () => 0,
      freemem: () => 0,
    },
    assert: {
      ok: function() {},
      equal: function() {},
      deepEqual: function() {},
      strictEqual: function() {},
      throws: function() {},
      notEqual: function() {},
      ifError: function() {},
    },
    dns: {
      lookup: (_host: string, cb: (err: Error | null, addr: string, family: number) => void) => { setTimeout(() => cb(null, '127.0.0.1', 4), 0); },
      resolve: (_host2: string, cb: (err: Error | null, addrs: string[]) => void) => { setTimeout(() => cb(null, ['127.0.0.1']), 0); },
    },
    buffer: bufferModule,
    Buffer: bufferModule,
    string_decoder: {
      StringDecoder: function() {
        this.write = function(x: any) { return typeof x === 'string' ? x : new TextDecoder().decode(x); };
        this.end = function(x: any) { return x ? this.write(x) : ''; };
      },
    },
    querystring: {
      parse: (s: string) => {
        const out: Record<string, string> = {};
        const q = String(s || '').replace(/^\?/, '');
        if (!q) return out;
        for (const pair of q.split('&')) {
          const [k, v] = pair.split('=');
          if (k) out[decodeURIComponent(k)] = v !== undefined ? decodeURIComponent(v) : '';
        }
        return out;
      },
      stringify: (obj: any) => Object.entries(obj || {}).map(([k, v]) => encodeURIComponent(String(k)) + '=' + encodeURIComponent(String(v))).join('&'),
    },
    tty: { isatty: () => false, ReadStream: function() {}, WriteStream: function() {} },
    net: {
      createServer: () => ({ listen: () => {}, close: () => {}, on: function() { return this; } }),
      createConnection: () => createEventEmitter(),
      Socket: function() { return createEventEmitter(); },
    },
    tls: {
      connect: () => createEventEmitter(),
      TLSSocket: function() { return createEventEmitter(); },
      createSecureContext: () => ({}),
    },
    child_process: {
      spawn: () => createEventEmitter(),
      exec: (_cmd: string, cb: (err: Error | null, stdout: string, stderr: string) => void) => setTimeout(() => cb(null, '', ''), 0),
      execSync: () => '',
    },
    cluster: { isWorker: false, isMaster: true, fork: function() {}, on: function() { return this; } },
    module: { Module: function() {} },
    vm: { runInNewContext: (code: string, sandbox: any) => { const fn = new Function(...Object.keys(sandbox || {}), code); return fn(...Object.values(sandbox || {})); } },
    perf_hooks: { performance: typeof (globalThis as any).performance !== 'undefined' ? (globalThis as any).performance : { now: () => Date.now() } },
    console,
  };
}

// 为给定的 webpack bundle 代码提供完整的沙箱环境
export function createSandboxRequire(): (id: string) => any {
  const polyfills = createNodePolyfills();
  return function(id: string): any {
    if (polyfills[id]) return polyfills[id];
    // 处理子路径，如 stream/transform, crypto/hash 等
    const base = id.split('/')[0];
    if (polyfills[base]) return polyfills[base];
    return {};
  };
}
