(function () {
  try {
    if (window.__MCJ_1688_ALBUM_HOOK_INSTALLED__) return;
    window.__MCJ_1688_ALBUM_HOOK_INSTALLED__ = true;

    var logPrefix = "[MCJ-1688-AlbumHook]";
    try { console.debug(logPrefix, "installing..."); } catch (_) {}

    var originalInsertBefore = Node.prototype.insertBefore;
    var originalAppendChild = Node.prototype.appendChild;
    var origSetAttribute = Element.prototype.setAttribute;

    var wrappedCbName = null;

    function saveAlbumData(data) {
      try {
        var el = document.getElementById('album-data');
        if (!el) {
          el = document.createElement('script');
          el.id = 'album-data';
          el.type = 'application/json';
          document.documentElement.appendChild(el);
        }
        el.textContent = '';
        el.textContent = JSON.stringify(data);
        try { console.debug(logPrefix, 'album-data saved'); } catch (_) {}
      } catch (err) {
        try { console.error(logPrefix, 'save album-data error', err); } catch (_) {}
      }
    }

    function extractCbName(src) {
      var cb = null;
      try {
        cb = new URL(src).searchParams.get('callback');
      } catch (e) {
        var m = src && src.match(/[?&]callback=([^&]+)/);
        cb = m && decodeURIComponent(m[1]);
      }
      return cb;
    }

    function isAlbumListJsonp(src) {
      return !!(src && typeof src === 'string' && src.indexOf('picman.1688.com/album/ajax/album_puller_ajax.json?') !== -1);
    }

    function wrapExistingCallback(cbName) {
      try {
        var fn = window[cbName];
        if (typeof fn === 'function') {
          if (wrappedCbName === cbName) return true;
          wrappedCbName = cbName;
          var original = fn;
          window[cbName] = function () {
            try { saveAlbumData(arguments[0]); } catch (_) {}
            try { return original.apply(this, arguments); } catch (e) { return undefined; }
          };
          try { console.debug(logPrefix, 'JSONP callback wrapped (existing):', cbName); } catch (_) {}
          return true;
        }
      } catch (e) {
        try { console.warn(logPrefix, 'wrapExistingCallback error', e); } catch (_) {}
      }
      return false;
    }

    function ensureWrap(cbName) {
      if (!cbName) return;
      if (wrappedCbName === cbName) return;

      // 1) try wrap immediately if already defined
      if (wrapExistingCallback(cbName)) return;

      // 2) define accessor to capture future assignment
      try {
        var hiddenKey = '__MCJ_CB_ORIG__' + cbName;
        if (!Object.prototype.hasOwnProperty.call(window, cbName)) {
          Object.defineProperty(window, cbName, {
            configurable: true,
            enumerable: true,
            get: function () { return window[hiddenKey]; },
            set: function (val) {
              try {
                if (typeof val === 'function') {
                  var original = val;
                  var wrapped = function () {
                    try { saveAlbumData(arguments[0]); } catch (_) {}
                    try { return original.apply(this, arguments); } catch (e) { return undefined; }
                  };
                  window[hiddenKey] = original;
                  // Replace accessor with concrete value (wrapped)
                  Object.defineProperty(window, cbName, {
                    configurable: true,
                    writable: true,
                    enumerable: true,
                    value: wrapped
                  });
                  wrappedCbName = cbName;
                  try { console.debug(logPrefix, 'JSONP callback wrapped (accessor):', cbName); } catch (_) {}
                } else {
                  window[hiddenKey] = val;
                }
              } catch (e) {
                try { console.error(logPrefix, 'accessor wrap error for', cbName, e); } catch (_) {}
                // Fallback to direct assignment
                window[cbName] = val;
              }
            }
          });
        }
      } catch (e) {
        try { console.warn(logPrefix, 'defineProperty failed for', cbName, e); } catch (_) {}
      }

      // 3) also schedule short retries in case accessor is overwritten early
      var attempts = 0;
      (function retry() {
        attempts++;
        if (wrapExistingCallback(cbName)) return;
        if (attempts < 200) setTimeout(retry, 10);
      })();
    }

    function tryHook(nodeOrSrc) {
      try {
        var src = typeof nodeOrSrc === 'string' ? nodeOrSrc : (nodeOrSrc && nodeOrSrc.src);
        if (!isAlbumListJsonp(src)) return;
        var cb = extractCbName(src);
        ensureWrap(cb);
      } catch (e) {
        try { console.error(logPrefix, 'tryHook error', e); } catch (_) {}
      }
    }

    // Hook DOM insertion APIs
    Node.prototype.insertBefore = function (newNode, referenceNode) {
      tryHook(newNode);
      return originalInsertBefore.call(this, newNode, referenceNode);
    };

    Node.prototype.appendChild = function (newNode) {
      tryHook(newNode);
      return originalAppendChild.call(this, newNode);
    };

    // Intercept setting of src via setAttribute
    Element.prototype.setAttribute = function (name, value) {
      if (this && this.nodeName === 'SCRIPT' && String(name).toLowerCase() === 'src') {
        tryHook(String(value));
      }
      return origSetAttribute.apply(this, arguments);
    };

    // Intercept direct assignment to script.src
    try {
      var desc = Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, 'src');
      if (desc && desc.set && desc.configurable) {
        Object.defineProperty(HTMLScriptElement.prototype, 'src', {
          configurable: true,
          enumerable: desc.enumerable,
          get: desc.get,
          set: function (v) {
            tryHook(String(v));
            return desc.set.call(this, v);
          }
        });
        try { console.debug(logPrefix, 'HTMLScriptElement.src setter hooked'); } catch (_) {}
      }
    } catch (e) {
      try { console.warn(logPrefix, 'hooking HTMLScriptElement.src failed', e); } catch (_) {}
    }

    // Observe dynamically added scripts and src changes
    try {
      var mo = new MutationObserver(function (mutations) {
        for (var i = 0; i < mutations.length; i++) {
          var m = mutations[i];
          if (m.type === 'childList') {
            var added = m.addedNodes;
            for (var j = 0; j < added.length; j++) {
              var n = added[j];
              if (n && n.nodeName === 'SCRIPT') {
                tryHook(n);
              }
            }
          } else if (m.type === 'attributes') {
            if (m.target && m.target.nodeName === 'SCRIPT' && m.attributeName === 'src') {
              tryHook(m.target);
            }
          }
        }
      });
      mo.observe(document.documentElement || document, {
        subtree: true,
        childList: true,
        attributes: true,
        attributeFilter: ['src']
      });
      try { console.debug(logPrefix, 'MutationObserver running'); } catch (_) {}
    } catch (e) {
      try { console.warn(logPrefix, 'MutationObserver failed', e); } catch (_) {}
    }

    try { console.debug(logPrefix, 'installed'); } catch (_) {}
  } catch (e) {
    try { console.error('[MCJ-1688-AlbumHook] fatal', e); } catch (_) {}
  }
})();