/*
 * AtomX 3.3.3 - Unlock + Install-Compat Patch
 *
 * Strategy:
 *   The cleanest bypass exploits the existing 2nd-argument branch of
 *   installPackageFirstCheck(packPath, abs).
 *
 *     _0x443043
 *       ? (st_install.pack.abs = _0x443043, installPackageStepFileSystem())
 *       : installationTempXHRObject.premiumPack && installationTempXHRObject.reqACheck
 *           ? changeShowHideBlurred(true, '.verifyInstallationPack', 3)
 *           : packageActivationServer(false, installationTempXHRObject);
 *
 *   When the second arg is truthy (as used by the subscription-install flow),
 *   the function skips BOTH the verify popup AND the activation server call,
 *   going straight to installPackageStepFileSystem(). That is the same path
 *   the original code uses after a server-validated success.
 *
 *   So we wrap installPackageFirstCheck to always supply a non-empty 2nd arg.
 *
 * Additionally:
 *   - Wrap initPackage with a diagnostic + fallback file-reader so any
 *     CANT_OPEN can be investigated and recovered from. Some CEP runtimes
 *     hand fs.readFile a string that fails (e.g. trailing whitespace from a
 *     Windows file dialog); the fallback retries with sync read and a few
 *     encoding variants.
 *   - Keep belt-and-suspenders overrides for packageActivationServer and the
 *     verify popup in case some other call site reaches them.
 *   - Hide subscription/lock UI and strip is_demo_lock for already-installed
 *     DEMO packs.
 *
 * Loaded LAST from index.html so it can reach the globals defined by the
 * obfuscated package.js / events.js / popups.js / app.js.
 */
(function () {
  'use strict';

  var TAG = '[atomx-unlock]';

  // ---- File-based logger so we can diagnose without DevTools ----
  var LOG_FILE = null;
  (function () {
    try {
      var os = require('os');
      var path = require('path');
      LOG_FILE = path.join(os.homedir(), 'atomx-unlock.log');
    } catch (e) {
      try { LOG_FILE = (process && process.env && process.env.TEMP ? process.env.TEMP + '\\atomx-unlock.log' : null); } catch (e2) {}
    }
  })();
  function fileLog(line) {
    if (!LOG_FILE) return;
    try {
      var fs = require('fs');
      var ts = new Date().toISOString();
      fs.appendFileSync(LOG_FILE, ts + ' ' + line + '\r\n');
    } catch (e) {}
  }
  function fmt(args) {
    var out = [];
    for (var i = 0; i < args.length; i++) {
      var a = args[i];
      if (a == null) { out.push(String(a)); continue; }
      if (typeof a === 'string') { out.push(a); continue; }
      try { out.push(JSON.stringify(a)); } catch (e) { out.push(String(a)); }
    }
    return out.join(' ');
  }
  function log() {
    var msg = fmt(arguments);
    try { console.log(TAG, msg); } catch (e) {}
    fileLog('LOG  ' + msg);
  }
  function warn() {
    var msg = fmt(arguments);
    try { console.warn(TAG, msg); } catch (e) {}
    fileLog('WARN ' + msg);
  }
  function tryFn(fn) { try { return fn(); } catch (e) { return undefined; } }

  // Write a header on every load
  fileLog('================================================================');
  fileLog('LOAD log_file=' + LOG_FILE + ' url=' + (location && location.href));

  // Visible startup banner so we can confirm the patch is loaded.
  log('script loaded, document.readyState =', document.readyState);

  // ---- Defensive fs-read helper used by the initPackage wrapper ----
  function readPackageFile(filePath, cb) {
    var fs = null;
    try { fs = (typeof fsmodule !== 'undefined' && fsmodule && fsmodule._fs) ? fsmodule._fs : require('fs'); } catch (e) {}
    if (!fs) { return cb(new Error('no fs module'), null); }

    // First: async read (matches original behavior exactly)
    try {
      fs.readFile(filePath, { encoding: 'binary' }, function (err, data) {
        if (!err && data != null) { return cb(null, data); }
        // Fallback 1: sync read, binary encoding
        try {
          var data2 = fs.readFileSync(filePath, { encoding: 'binary' });
          return cb(null, data2);
        } catch (e2) {}
        // Fallback 2: sync read, no encoding -> Buffer -> latin1
        try {
          var buf = fs.readFileSync(filePath);
          var s = buf.toString('binary');
          return cb(null, s);
        } catch (e3) {}
        // Fallback 3: trim path (strip trailing whitespace/zero-width chars some dialogs add)
        try {
          var trimmed = String(filePath).replace(/[\s\u200b\u200e\u200f]+$/, '');
          if (trimmed && trimmed !== filePath) {
            var data4 = fs.readFileSync(trimmed, { encoding: 'binary' });
            return cb(null, data4);
          }
        } catch (e4) {}
        return cb(err || new Error('all read fallbacks failed'), null);
      });
    } catch (eOuter) {
      cb(eOuter, null);
    }
  }

  // ---- Core patches applied once original globals are defined ----
  function applyCorePatches() {
    if (typeof installPackageFirstCheck !== 'function') return false;
    if (window.__atomxUnlockPatched) return true;
    window.__atomxUnlockPatched = true;

    // 1) installPackageFirstCheck: always pass a truthy 2nd arg.
    //    This routes the install through the abs/subscription branch which
    //    skips both the verify popup AND the activation server call.
    var origIPFC = window.installPackageFirstCheck;
    window.installPackageFirstCheck = function (packPath, abs) {
      var force = abs ? abs : 'unlocked-' + Date.now();
      log('installPackageFirstCheck(', packPath, ',', force, ')');
      return origIPFC.call(this, packPath, force);
    };

    // 2) initPackage: wrap with diagnostic + fallback reader so we can
    //    survive transient CANT_OPEN errors and capture the real reason.
    if (typeof window.initPackage === 'function') {
      var origInit = window.initPackage;
      window.initPackage = function (filePath, callback) {
        log('---- initPackage CALLED ----');
        log('  path        =', JSON.stringify(filePath));
        log('  path.length =', String(filePath).length);
        try {
          var fs = require('fs');
          var stat = null;
          try { stat = fs.statSync(filePath); } catch (eS) { log('  statSync error:', eS && eS.code, eS && eS.message); }
          if (stat) log('  size_bytes =', stat.size);

          // Read the file ourselves to characterize it
          try {
            var rawBuf = fs.readFileSync(filePath);
            log('  read OK    =', rawBuf.length, 'bytes');

            // Head bytes (hex + ascii)
            var headHex = rawBuf.slice(0, 24).toString('hex');
            var headAscii = rawBuf.slice(0, 32).toString('latin1').replace(/[^\x20-\x7e]/g, '.');
            log('  head_hex   =', headHex);
            log('  head_ascii =', headAscii);

            // Marker check (latin1)
            var dataStr = rawBuf.toString('latin1');
            var hasONE = (typeof pdGlobalSplitOne === 'string') ? dataStr.indexOf(pdGlobalSplitOne) : 'undef';
            var hasTWO = (typeof pdGlobalSplitTwo === 'string') ? dataStr.indexOf(pdGlobalSplitTwo) : 'undef';
            var hasJSX = (typeof oldAtomFPiece    === 'string') ? dataStr.indexOf(oldAtomFPiece)    : 'undef';
            var hasIEND = dataStr.indexOf('IEND');
            log('  marker_ONE  at =', hasONE);
            log('  marker_TWO  at =', hasTWO);
            log('  marker_JSX  at =', hasJSX);
            log('  PNG_IEND    at =', hasIEND);
            log('  pdGlobalSplitOne defined =', typeof pdGlobalSplitOne, ' len=', (typeof pdGlobalSplitOne === 'string' ? pdGlobalSplitOne.length : 0));
          } catch (eR) {
            log('  read error :', eR && eR.code, eR && eR.message);
          }

          var exists = false;
          try {
            if (typeof fsmodule !== 'undefined' && fsmodule.exists) {
              exists = !!fsmodule.exists(filePath);
            }
          } catch (e) {}
          log('  fsmodule.exists =', exists);
        } catch (eDiag) { log('diag error:', eDiag && eDiag.message); }
        return origInit.call(this, filePath, function (result, err) {
          if (err === 'CANT_OPEN') {
            warn('initPackage original got CANT_OPEN, retrying via fallback reader');
            return readPackageFile(filePath, function (e2, data) {
              if (e2) {
                warn('fallback reader also failed:', e2 && e2.message);
                return callback(false, 'CANT_OPEN');
              }
              warn('fallback succeeded; replaying initPackage logic with read bytes (', data.length, 'bytes)');
              try {
                // Replay the marker-based parsing the original initPackage does.
                var hasOne = (typeof pdGlobalSplitOne === 'string') && data.indexOf(pdGlobalSplitOne) !== -1;
                var hasJsx = (typeof oldAtomFPiece === 'string') && data.indexOf(oldAtomFPiece) !== -1;
                if (hasOne && typeof PDRefractoring === 'function') {
                  var parts = PDRefractoring(data);
                  try {
                    var settings = JSON.parse(parts[2]);
                    return callback({
                      method: 'JSON',
                      full_content: settings,
                      settings: settings.settings,
                      structure: settings.structure,
                      header_bytes: parts[1],
                      pack_hash: parts[0]
                    });
                  } catch (eParse) {
                    return callback(false, 'WRONG_PACKPRINT_PREF');
                  }
                } else if (hasJsx) {
                  // Legacy format requires JSX call; just bail to original error path.
                  return callback(false, 'CANT_OPEN');
                } else {
                  return callback(false, 'CORRUPTED_PACK');
                }
              } catch (eReplay) {
                warn('replay logic failed:', eReplay && eReplay.message);
                return callback(false, 'CANT_OPEN');
              }
            });
          }
          if (err) warn('initPackage error code:', err);
          return callback(result, err);
        });
      };
    }

    // 3) Hard short-circuit packageActivationServer just in case some other
    //    code path reaches it.
    if (typeof window.packageActivationServer === 'function') {
      window.packageActivationServer = function (isPremium, packData) {
        log('packageActivationServer short-circuit, isPremium=', isPremium);
        try {
          if (!isPremium) tryFn(function () { setupProgressBar(false, '_STOP'); });
          tryFn(function () {
            if (typeof window.changeShowHideBlurredOriginal === 'function')
              window.changeShowHideBlurredOriginal(false, '.verifyInstallationPack', 3);
          });
          var fake = { check: true, valid: true };
          tryFn(function () {
            setActivationGatekeeper((packData && (packData.appID || packData.appid)) || '', fake, JSON.stringify(fake));
          });
          tryFn(function () { installPackageStepFileSystem(); });
        } catch (e) { warn('activate short-circuit err', e); }
      };
    }

    // 4) Wrap changeShowHideBlurred: never show the verify popup or
    //    subscription wall, but otherwise leave it intact.
    if (typeof window.changeShowHideBlurred === 'function') {
      window.changeShowHideBlurredOriginal = window.changeShowHideBlurred;
      window.changeShowHideBlurred = function (show, selector) {
        try {
          if (show && (selector === '.verifyInstallationPack' || selector === 'verify-panel')) {
            log('blocked verify popup, auto-completing');
            tryFn(function () { installPackageStepFileSystem(); });
            return;
          }
          if (show && (selector === '.subscriptionContent' ||
                       selector === '.bsPackageSector[section="subscriptionContent"]')) {
            log('blocked subscription wall');
            return;
          }
        } catch (e) {}
        return window.changeShowHideBlurredOriginal.apply(this, arguments);
      };
    }

    // 5) No-op the "subscription required" UI.
    if (typeof window.packageIsNoSubscriptionStyle === 'function') {
      window.packageIsNoSubscriptionStyle = function () { log('packageIsNoSubscriptionStyle suppressed'); };
    }

    // 6) Fake auth state truthy.
    tryFn(function () { window.tempLocalPersonalizedAuthSubscriptionStatus = true; });
    tryFn(function () {
      if (!window.tempLocalPersonalizedAuthSystemData || !window.tempLocalPersonalizedAuthSystemData.usid) {
        window.tempLocalPersonalizedAuthSystemData = Object.assign(
          window.tempLocalPersonalizedAuthSystemData || {},
          { usid: 'unlocked-local', email: 'local@local', name: 'Local User' }
        );
      }
    });

    // 7) jQuery hasClass override so DEMO-locked items remain functional.
    if (typeof window.jQuery === 'function' && jQuery.fn && jQuery.fn.hasClass) {
      var origHasClass = jQuery.fn.hasClass;
      jQuery.fn.hasClass = function (cls) {
        if (cls === 'is_demo_lock') return false;
        return origHasClass.apply(this, arguments);
      };
    }

    log('core patches applied');
    return true;
  }

  // ---- CSS overrides ----
  function injectCss() {
    if (document.getElementById('atomxUnlockCss')) return;
    var style = document.createElement('style');
    style.id = 'atomxUnlockCss';
    style.textContent = [
      'verify-panel { display:none !important; }',
      '.bsPackageSector[section="subscriptionContent"] { display:none !important; }',
      'sup.is_premium_group, sup.is_demo_pack { display:none !important; }',
      '.element.is_demo_lock { opacity: 1 !important; pointer-events: auto !important; }',
      '.element.is_demo_lock .lock, .element.is_demo_lock .demo_lock { display:none !important; }'
    ].join('\n');
    (document.head || document.documentElement).appendChild(style);
  }

  // ---- Strip is_demo_lock from DOM (continuous) ----
  function watchDemoLocks() {
    if (window.__atomxLockObserver) return;
    var strip = function (root) {
      try {
        if (!root || !root.querySelectorAll) return;
        var nodes = (root.classList && root.classList.contains('is_demo_lock')) ? [root] : [];
        var qs = root.querySelectorAll ? root.querySelectorAll('.is_demo_lock') : [];
        for (var i = 0; i < qs.length; i++) nodes.push(qs[i]);
        for (var j = 0; j < nodes.length; j++) nodes[j].classList.remove('is_demo_lock');
      } catch (e) {}
    };
    strip(document);
    var obs = new MutationObserver(function (muts) {
      for (var i = 0; i < muts.length; i++) {
        var m = muts[i];
        for (var k = 0; k < m.addedNodes.length; k++) strip(m.addedNodes[k]);
        if (m.type === 'attributes' && m.target && m.target.classList &&
            m.target.classList.contains('is_demo_lock')) {
          m.target.classList.remove('is_demo_lock');
        }
      }
    });
    obs.observe(document.documentElement, {
      childList: true, subtree: true, attributes: true, attributeFilter: ['class']
    });
    window.__atomxLockObserver = obs;
  }

  function init() {
    injectCss();
    watchDemoLocks();
    if (applyCorePatches()) return;
    var tries = 0;
    var iv = setInterval(function () {
      tries++;
      if (applyCorePatches() || tries > 400) {
        clearInterval(iv);
        if (tries > 400) warn('gave up waiting for core fns');
      }
    }, 25);
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
})();
