// This is the dedicated Emscripten worker script.

const messageQueue = [];

// Define the Module object for Emscripten
var Module = { // Use a different name to avoid confusion
  locateFile: (path) => {
    console.log(`EmscriptenWorker: locateFile called for path: ${path}`);
    if (path.endsWith('.wasm')) {
      console.log("EmscriptenWorker: Returning /web-randomx.wasm for WASM file.");
      return '/web-randomx.wasm'; // WASM file is in public/
    }
    return path;
  },
  onRuntimeInitialized: () => {
    console.log("EmscriptenWorker: Emscripten runtime initialized.");
    try {
      hashingLogic = new HashingWrapper(Module); // Module is now the actual Emscripten module
      console.log("EmscriptenWorker: HashingWrapper instantiated.");
      self.postMessage({ type: 'emscripten-initialized' }); // Notify parent worker

      // Process any queued messages
      while (messageQueue.length > 0) {
        processMessage(messageQueue.shift());
      }
    } catch (e) {
      console.error("EmscriptenWorker: Error instantiating HashingWrapper:", e);
    }
  }
};
console.log("EmscriptenWorker: Emscripten module config defined.");

// Import the Emscripten-generated web-randomx.js
try {
  self.importScripts('/web-randomx.js');
  console.log("EmscriptenWorker: web-randomx.js imported successfully.");

} catch (e) {
  console.error("EmscriptenWorker: Error importing or executing web-randomx.js:", e);
}

let hashingLogic = null;

class HashingWrapper {
  constructor(module) {
    this.module = module;

    // Available exported functions from WASM
    this.initCacheFunc   = this.module._web_randomx_init_cache;
    this.createVmFunc    = this.module._web_randomx_create_vm;
    this.hashFunc        = this.module._web_randomx_hash;
    this.releaseCacheFunc= this.module._web_randomx_release_cache;
    this.destroyVmFunc   = this.module._web_randomx_destroy_vm;

    const exportedKeys = Object.keys(this.module).filter(key =>
      key.startsWith('_web') || key.startsWith('_randomx') || key.startsWith('_')
    );
    console.log("EmscriptenWorker: DIAGNOSIS - Available exported keys:", exportedKeys);

    if (!this.initCacheFunc || !this.createVmFunc || !this.hashFunc) {
      console.error("EmscriptenWorker: CRITICAL: Required functions not found. Hashing will fail.");
    }

    // Internal state
    this.currentJob = null;
    this.throttleWait = 0;
    this.throttledStart = 0;
    this.throttledHashes = 0;
    this.workThrottledBound = this.workThrottled.bind(this);
    this.target = new Uint8Array(32);
    this.input = null;
    this.output = null;
    this.seed_input = null;
    this.blob = null;
    this.seed_blob = null;
    this.variant = 0;
    this.height = 0;
    this.isWorking = false;
  }


  allocateMemory() {
    if (this.input && this.input.byteLength > 0) return;
    
    try {
      const mallocFunc = this.module._malloc || this.module.malloc;
      
      if (this.module.HEAPU8 && this.module.HEAPU8.buffer && mallocFunc) {
        // Allocate space for the job blob (256 bytes is a common max)
        this.input = new Uint8Array(this.module.HEAPU8.buffer, mallocFunc(256), 256);
        // Allocate space for the 32-byte hash output
        this.output = new Uint8Array(this.module.HEAPU8.buffer, mallocFunc(32), 32);
        // Allocate space for the 32-byte seed hash input
        this.seed_input = new Uint8Array(this.module.HEAPU8.buffer, mallocFunc(32), 32);
        
        if (this.input.byteOffset === 0) {
            console.error("EmscriptenWorker: Malloc returned 0. Allocation failed.");
            this.input = null;
        }
      } else {
         console.error(`EmscriptenWorker: Memory allocation failed. _malloc is missing.`);
      }
    } catch (e) {
      console.error("EmscriptenWorker: Alloc error:", e);
    }
  }

  hexToBytes(hex) {
    const len = hex.length / 2;
    let bytes = new Uint8Array(len);
    for (let i = 0; i < len; ++i) {
      bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
    }
    return bytes;
  }

  bytesToHex(bytes) {
    let hex = '';
    for (let i = 0; i < bytes.length; ++i) {
      hex += (bytes[i] >>> 4).toString(16);
      hex += (15 & bytes[i]).toString(16);
    }
    return hex;
  }

  meetsTarget(output, target) {
    let isZero = true;
    for (let j = 0; j < output.length; j++) {
      if (output[j] !== 0) {
        isZero = false;
        break;
      }
    }
    if (isZero) return false;

    for (let i = 1; i <= target.length; ++i) {
      if (output[output.length - i] > target[target.length - i]) return false;
      if (output[output.length - i] < target[target.length - i]) return true;
    }
    return false;
  }

  setJob(data) {
    console.log('EmscriptenWorker: Setting new job:', data);
    this.allocateMemory();
    if (!this.input) {
      console.error("EmscriptenWorker: setJob aborted, memory not allocated.");
      return;
    }
    try {
      this.currentJob = data;
      this.blob = this.hexToBytes(data.blob);
      this.input.set(this.blob);
      
      const targetBytes = this.hexToBytes(data.target);
      if (targetBytes.length <= 8) {
        for (let i = 1; i <= targetBytes.length; ++i) {
          this.target[this.target.length - i] = targetBytes[targetBytes.length - i];
        }
        for (let i = 0; i < this.target.length - targetBytes.length; ++i) {
          this.target[i] = 255;
        }
      } else {
        this.target.set(targetBytes);
      }
      
      this.variant = data.variant === undefined ? 0 : data.variant;
      this.height = data.height === undefined ? 0 : data.height;
      this.seed_blob = this.hexToBytes(data.seed_hash);
      this.seed_input.set(this.seed_blob);

     if (this.initCacheFunc) {
          try {
              console.log('EmscriptenWorker: Initializing cache.');
              this.initCacheFunc(this.variant, BigInt(this.height), this.seed_input.byteOffset);
              console.log('EmscriptenWorker: Cache initialized.');
          } catch (initError) {
              console.error("EmscriptenWorker: RandomX VM initialization failed in WASM (setJob).", initError);
          }
     } else {
          console.error("EmscriptenWorker: Critical: Initialization function not found. Hashing will fail.");
     }

    } catch (e) {
      console.error("Job set error:", e);
    }
  }

  now() {
    return (self.performance ? self.performance.now() : Date.now());
  }

  hash(input, output, byteLength, variant, height, seed) {
    if (!this.input || this.input.byteLength === 0 || !this.hashFunc) return 0;

    try {
      const nonce = 4294967295 * Math.random() + 1 >>> 0;
      this.input[39] = (4278190080 & nonce) >> 24;
      this.input[40] = (16711680 & nonce) >> 16;
      this.input[41] = (65280 & nonce) >> 8;
      this.input[42] = (255 & nonce) >> 0;

      this.hashFunc(this.variant, BigInt(this.height), seed.byteOffset, input.byteOffset, byteLength, output.byteOffset);
      return 1;
    } catch (e) {
      // The crash is due to uninitialized VM, which is fixed by recompiling (Step 1)
      console.error('EmscriptenWorker: Error during hash calculation:', e);
      return 0;
    }
  }

  work() {
    if (!this.isWorking || !this.currentJob) {
      console.log('EmscriptenWorker: Work loop stopped. isWorking:', this.isWorking, 'currentJob:', this.currentJob);
      return;
    }
    
    this.allocateMemory();
    if (!this.input) {
         setTimeout(() => this.work(), 100); 
         return;
    }

    const workStart = this.now();
    let hashes = 0;
    let ifMeetTarget = false;
    let interval = 0;
    let loopCount = 0;

    while (!ifMeetTarget && interval < 1000 && loopCount < 100000) {
      hashes += this.hash(this.input, this.output, this.blob.length, this.variant, this.height, this.seed_input);
      ifMeetTarget = this.meetsTarget(this.output, this.target);
      interval = this.now() - workStart;
      loopCount++;
    }

    const effectiveInterval = interval > 0 ? interval : 1;
    const hashesPerSecond = hashes / (effectiveInterval / 1e3);

    if (ifMeetTarget) {
      const nonce = this.bytesToHex(this.input.subarray(39, 43));
      const result = this.bytesToHex(this.output);
      self.postMessage({
        type: 'hash-found',
        payload: {
          hashesPerSecond: hashesPerSecond,
          hashes: hashes,
          job_id: this.currentJob.job_id,
          nonce: nonce,
          result: result
        }
      });
    } else {
      self.postMessage({
        type: 'hash-stats',
        payload: {
          hashesPerSecond: hashesPerSecond,
          hashes: hashes
        }
      });
    }

    if (this.isWorking) {
        setTimeout(() => this.work(), 0);
    }
  }

  workThrottled() {
    console.log('EmscriptenWorker: Starting throttled work loop.');
    if (!this.isWorking || !this.currentJob) {
      console.log('EmscriptenWorker: Throttled work loop stopped. isWorking:', this.isWorking, 'currentJob:', this.currentJob);
      return;
    }

    this.allocateMemory();
    if (!this.input) {
        setTimeout(this.workThrottledBound, 100);
        return;
    }
    
    const WORK_BURST_MS = 50;
    const throttleRatio = 1 / (1 - this.throttleWait) - 1;
    const SLEEP_TIME_MS = WORK_BURST_MS * throttleRatio; 
    
    const burstStart = this.now();
    if (this.throttledStart === 0) this.throttledStart = burstStart;

    let hashesInBurst = 0;
    let targetFound = false;
    
    while ((this.now() - burstStart) < WORK_BURST_MS) {
        hashesInBurst += this.hash(this.input, this.output, this.blob.length, this.variant, this.height, this.seed_input);
        if (this.meetsTarget(this.output, this.target)) {
            targetFound = true;
            break;
        }
    }
    this.throttledHashes += hashesInBurst;

    const totalInterval = this.now() - this.throttledStart;
    const effectiveTotal = totalInterval > 0 ? totalInterval : 1;
    const hashesPerSecond = this.throttledHashes / (effectiveTotal / 1e3);

    if (targetFound) {
      const nonce = this.bytesToHex(this.input.subarray(39, 43));
      const result = this.bytesToHex(this.output);
      
      self.postMessage({
        type: 'hash-found',
        payload: {
          hashesPerSecond: hashesPerSecond,
          hashes: this.throttledHashes,
          job_id: this.currentJob.job_id,
          nonce: nonce,
          result: result
        }
      });
      this.throttledHashes = 0;
      this.throttledStart = 0;
      setTimeout(this.workThrottledBound, 0);

    } else if (totalInterval > 1000) {
      self.postMessage({
        type: 'hash-stats',
        payload: {
          hashesPerSecond: hashesPerSecond,
          hashes: this.throttledHashes
        }
      });
      // TYPO FIX: Changed 'thisottledHashes' to 'this.throttledHashes'
      this.throttledHashes = 0; 
      this.throttledStart = 0;
      setTimeout(this.workThrottledBound, 0);

    } else {
      const delay = Math.max(1, SLEEP_TIME_MS);
      setTimeout(this.workThrottledBound, delay);
    }
  }
}


// This worker will receive messages from its parent worker (miner.worker.js)
function processMessage(data) {
  const { type, payload } = data;
  console.log('EmscriptenWorker: Processing message:', data);

  if (!hashingLogic) {
    console.warn("EmscriptenWorker: Hashing logic not yet initialized, queueing message.");
    messageQueue.push(data);
    return;
  }

  switch (type) {
    case 'set-job':
      hashingLogic.setJob(payload.job);
      hashingLogic.throttleWait = 1 / (1 - payload.throttle) - 1; // Set throttle
      hashingLogic.isWorking = true;
      hashingLogic.work();
      break;
    case 'start-work':
      hashingLogic.isWorking = true;
      hashingLogic.work();
      break;
    case 'start-throttled-work':
      hashingLogic.isWorking = true;
      hashingLogic.workThrottled();
      break;
    case 'stop-hashing':
      hashingLogic.isWorking = false;
      break;
    case 'set-throttle':
      hashingLogic.throttleWait = 1 / (1 - payload) - 1;
      break;
    case 'set-threads':
      // Emscripten module might not directly support setting threads from here
      console.warn("EmscriptenWorker: set-threads not directly supported by HashingWrapper.");
      break;
    default:
      console.log("EmscriptenWorker: Unknown message type", type);
      break;
  }
}

self.addEventListener('message', (event) => {
  processMessage(event.data);
});

console.log("EmscriptenWorker: Script loaded.");
