/**
 * PrimeBridge Engine — Orchestration Layer
 * ===========================================
 * Load order (see front.blade.php):
 *   helpers.js → random.js → time.js → modal.js → events.js →
 *   notifications.js → ENGINE.JS → app.js
 *
 * The engine orchestrates the notification system by:
 *   1. Loading event data from live-events.json via PrimeBridge.events
 *   2. Controlling the notification feed timing (two modes)
 *   3. Providing start/stop/resume controls
 *   4. Feeding real event data into the notification renderer
 *   5. Logging and telemetry for debugging
 *
 * Modes:
 *   enhanced (default) — Loads real event data but lets the existing
 *     notifications.js auto-feed run. Safe, non-disruptive.
 *   orchestrated — Stops the built-in feed and takes over with
 *     adaptive timing (varies between minDelay–maxDelay ms) and
 *     real event data from PrimeBridge.events.
 *
 * @namespace PrimeBridge.engine
 * @version 1.0.0
 */
(function () {
  'use strict';

  /* ------------------------------------------------------------------ */
  /*  Namespace                                                          */
  /* ------------------------------------------------------------------ */

  window.PrimeBridge = window.PrimeBridge || {};

  /* ------------------------------------------------------------------ */
  /*  Engine Definition                                                   */
  /* ------------------------------------------------------------------ */

  /**
   * PrimeBridge.engine — Conductor of the notification system.
   *
   * @type {Object}
   * @property {boolean}  _initialized       - Whether init() has completed
   * @property {boolean}  _eventsLoadAttempted - Whether we attempted to load events
   * @property {boolean}  _eventsLoaded      - Whether events loaded successfully
   * @property {number|null} _intervalId     - Orchestration interval handle
   * @property {number}   _totalFired        - Count of notifications fired by engine
   * @property {Object}   _config            - Current configuration
   * @property {Function|null} onNotification - Callback before each notification
   * @property {Function|null} onError        - Error callback
   */
  PrimeBridge.engine = {

    /* ---------------------------------------------------------------- */
    /*  State                                                             */
    /* ---------------------------------------------------------------- */

    /** @private */
    _initialized: false,

    /** @private */
    _eventsLoadAttempted: false,

    /** @private */
    _eventsLoaded: false,

    /** @private */
    _intervalId: null,

    /** @private */
    _orchestrateTimerId: null,

    /** @private */
    _totalFired: 0,

    /**
     * Default configuration.
     * @private
     * @type {Object}
     */
    _config: {
      /** @type {string} 'enhanced' | 'orchestrated' */
      mode: 'enhanced',
      /** @type {number} Minimum ms between notifications (orchestrated mode) */
      minDelay: 3000,
      /** @type {number} Maximum ms between notifications (orchestrated mode) */
      maxDelay: 8000,
      /** @type {null|string[]} null = all types, or array like ['deposit', 'withdrawal'] */
      eventTypes: null,
      /** @type {boolean} Start automatically after init() */
      autoStart: true,
      /** @type {boolean} Log to console when true */
      debug: false
    },

    /**
     * Weighted event type distribution.
     *
     * Duplicates represent probability weight — the more times a type
     * appears, the more likely it is to be selected. Modeled after the
     * legacy engine's weightedCategories array but adapted for the
     * new 8-type event system.
     * @private
     * @type {string[]}
     */
    WEIGHTED_TYPES: [
      'deposit', 'deposit', 'deposit', 'deposit', 'deposit', 'deposit', 'deposit', 'deposit',
      'withdrawal', 'withdrawal', 'withdrawal', 'withdrawal',
      'registration', 'registration',
      'managedPortfolio',
      'selfTrading',
      'sectors',
      'capitalInstruments',
      'bonus', 'bonus'
    ],

    /* ---------------------------------------------------------------- */
    /*  Callbacks                                                         */
    /* ---------------------------------------------------------------- */

    /**
     * Callback invoked immediately before the engine fires a notification.
     * Receives the resolved event data object. Return false to cancel
     * the notification.
     * @type {Function|null}
     */
    onNotification: null,

    /**
     * Callback invoked when an error occurs during event loading
     * or orchestration.
     * @type {Function|null}
     */
    onError: null,

    /* ---------------------------------------------------------------- */
    /*  Lifecycle                                                         */
    /* ---------------------------------------------------------------- */

    /**
     * Initialize the engine: load event data, then auto-start if configured.
     *
     * Safe to call multiple times — subsequent calls are no-ops once
     * the engine is initialized.
     *
     * @returns {Promise<void>}
     *
     * @example
     *   await PrimeBridge.engine.init();
     */
    init: async function () {
      if (this._initialized) {
        this._log('[PrimeBridge.engine] Already initialized, skipping');
        return;
      }

      this._log('[PrimeBridge.engine] Initializing...');

      // 1. Load event data from live-events.json via PrimeBridge.events
      await this._loadEventsWithTimeout();

      // 2. If events failed to load, the existing notifications.js auto-feed
      //    continues running as-is (old hardcoded pattern).

      this._initialized = true;

      // 3. Auto-start if configured
      if (this._config.autoStart) {
        this.start();
      }

      this._log(
        '[PrimeBridge.engine] Initialized in "' + this._config.mode + '" mode. ' +
        'Events loaded: ' + this._eventsLoaded +
        ', Weighted types loaded: ' + this.WEIGHTED_TYPES.length + ' entries'
      );
    },

    /**
     * Attempt to load events via PrimeBridge.events.loadEvents() with
     * a 10-second timeout. On failure, the existing feed is left untouched.
     *
     * @private
     * @returns {Promise<void>}
     */
    _loadEventsWithTimeout: async function () {
      if (this._eventsLoadAttempted) return;
      this._eventsLoadAttempted = true;

      // Check that PrimeBridge.events exists and has loadEvents
      if (
        !window.PrimeBridge.events ||
        typeof PrimeBridge.events.loadEvents !== 'function'
      ) {
        this._log(
          '[PrimeBridge.engine] PrimeBridge.events not available — ' +
          'events module may not be loaded. Falling back to old pattern.',
          'warn'
        );
        this._fireError(
          new Error('PrimeBridge.events.loadEvents is not a function')
        );
        return;
      }

      // Create a promise that rejects after 10 seconds
      var self = this;

      var timeoutPromise = new Promise(function (_, reject) {
        setTimeout(function () {
          reject(new Error('Event data fetch timed out after 10 seconds'));
        }, 10000);
      });

      try {
        await Promise.race([
          PrimeBridge.events.loadEvents('assets/data/live-events.json'),
          timeoutPromise
        ]);

        this._eventsLoaded = PrimeBridge.events.isLoaded
          ? PrimeBridge.events.isLoaded()
          : true;

        this._log(
          '[PrimeBridge.engine] Events loaded successfully. ' +
          'Types: ' + (
            PrimeBridge.events.getAllTypes
              ? PrimeBridge.events.getAllTypes().join(', ')
              : 'N/A'
          )
        );
      } catch (err) {
        this._eventsLoaded = false;
        this._log(
          '[PrimeBridge.engine] Failed to load events: ' + err.message,
          'warn'
        );
        this._fireError(err);
        // Fall through — old feed continues, engine operates in
        // transparent pass-through mode.
      }
    },

    /* ---------------------------------------------------------------- */
    /*  Feed Control                                                      */
    /* ---------------------------------------------------------------- */

    /**
     * Start (or restart) the notification feed.
     *
     * In "enhanced" mode (default): the existing notifications.js auto-feed
     * is left running. The engine just loads event data for other consumers.
     *
     * In "orchestrated" mode: the engine stops the built-in feed and
     * takes over with adaptive timing using real event data from
     * PrimeBridge.events.
     *
     * @param {Object}  [config]          - Optional config overrides.
     * @param {string}  [config.mode]     - 'enhanced' or 'orchestrated'
     * @param {number}  [config.minDelay] - Minimum ms between notifications
     * @param {number}  [config.maxDelay] - Maximum ms between notifications
     * @param {Array}   [config.eventTypes] - Filter to specific event types
     * @param {boolean} [config.debug]    - Enable console logging
     * @returns {boolean} True if the feed was started successfully
     *
     * @example
     *   // Start in enhanced mode (default)
     *   PrimeBridge.engine.start();
     *
     * @example
     *   // Start in orchestrated mode with custom timing
     *   PrimeBridge.engine.start({
     *     mode: 'orchestrated',
     *     minDelay: 4000,
     *     maxDelay: 10000
     *   });
     */
    start: function (config) {
      // Merge optional config
      if (config && typeof config === 'object') {
        if (config.mode !== undefined) {
          this._config.mode = config.mode;
        }
        if (config.minDelay !== undefined) {
          this._config.minDelay = config.minDelay;
        }
        if (config.maxDelay !== undefined) {
          this._config.maxDelay = config.maxDelay;
        }
        if (config.eventTypes !== undefined) {
          this._config.eventTypes = config.eventTypes;
        }
        if (config.debug !== undefined) {
          this._config.debug = config.debug;
        }
      }

      // Ensure we are not already running in orchestrated mode
      this._clearOrchestration();

      if (this._config.mode === 'orchestrated') {
        return this._startOrchestrated();
      }

      // Enhanced mode — just log and let the existing auto-feed run
      this._log(
        '[PrimeBridge.engine] Enhanced mode active. ' +
        'Notifications.js auto-feed is running with ' +
        (this._eventsLoaded ? 'event-data-driven content' : 'hardcoded content')
      );
      return true;
    },

    /**
     * Start the fully orchestrated feed with adaptive timing.
     *
     * Stops the existing notifications.js auto-feed, then runs its own
     * loop using PrimeBridge.events.getRandomEvent() and
     * PrimeBridge.notifications.createCard().
     *
     * @private
     * @returns {boolean} True if orchestration started
     */
    _startOrchestrated: function () {
      // Stop the built-in auto-feed
      if (
        window.PrimeBridge.notifications &&
        typeof PrimeBridge.notifications.stop === 'function'
      ) {
        PrimeBridge.notifications.stop();
        this._log('[PrimeBridge.engine] Stopped notifications.js auto-feed');
      }

      // Verify we have the dependencies we need
      if (
        !window.PrimeBridge.notifications ||
        typeof PrimeBridge.notifications.createCard !== 'function'
      ) {
        this._log(
          '[PrimeBridge.engine] PrimeBridge.notifications.createCard not available — ' +
          'cannot start orchestrated feed',
          'error'
        );
        return false;
      }

      this._intervalId = 1; // Mark as running (non-null truthy)

      this._log(
        '[PrimeBridge.engine] Orchestrated mode started. ' +
        'Delay range: ' + this._config.minDelay + '–' + this._config.maxDelay + 'ms. ' +
        'Events loaded: ' + this._eventsLoaded
      );

      // Fire the first notification immediately
      this._orchestrateTick();
      return true;
    },

    /**
     * Execute a single orchestration tick: pick an event and schedule
     * the next tick with adaptive delay.
     *
     * @private
     * @returns {void}
     */
    _orchestrateTick: function () {
      // Stop if we've been stopped externally
      if (!this._intervalId) return;

      var self = this;

      // --- Pick event data ---
      var eventData = this._pickEventData();

      if (eventData) {
        // Fire the onNotification callback if set
        if (typeof this.onNotification === 'function') {
          var shouldProceed = this.onNotification(eventData);
          if (shouldProceed === false) {
            // Callback cancelled — still schedule next tick
            this._scheduleNextTick();
            return;
          }
        }

        // Render the notification
        try {
          PrimeBridge.notifications.createCard(eventData);
          this._totalFired++;
          this._log(
            '[PrimeBridge.engine] Fired notification #' + this._totalFired +
            ' (' + eventData.type + '): ' + (eventData.title || 'Untitled')
          );
        } catch (err) {
          this._log(
            '[PrimeBridge.engine] Error rendering notification: ' + err.message,
            'error'
          );
          this._fireError(err);
        }
      } else {
        this._log(
          '[PrimeBridge.engine] No event data available — skipping tick',
          'warn'
        );
      }

      // Schedule next tick with adaptive delay
      this._scheduleNextTick();
    },

    /**
     * Pick event data from the available sources.
     *
     * If events are loaded, uses PrimeBridge.events.getRandomEvent().
     * Falls back to a procedurally generated event if events aren't loaded
     * but PrimeBridge.random is available.
     * Returns null if nothing can be generated.
     *
     * @private
     * @returns {Object|null} Event data object or null
     */
    _pickEventData: function () {
      // Prefer real event data
      if (
        this._eventsLoaded &&
        window.PrimeBridge.events &&
        typeof PrimeBridge.events.getRandomEvent === 'function'
      ) {
        var eventType = this._getWeightedType();

        var resolved = PrimeBridge.events.getRandomEvent(eventType);
        if (resolved) {
          // Ensure it has the fields expected by createCard
          // Determine the best modal title
          var modalTitle = resolved.modalTitle || '';
          if (!modalTitle) {
            var titleMap = {
              deposit: 'Deposit Confirmed',
              withdrawal: 'Withdrawal Processed',
              registration: 'Account Created',
              managedPortfolio: 'Portfolio Activated',
              selfTrading: 'Trade Opened',
              sectors: 'Sector Investment',
              capitalInstruments: 'Instrument Purchase',
              bonus: 'Bonus Awarded'
            };
            modalTitle = titleMap[resolved.type] || 'Live Transaction';
          }

          return {
            type: resolved.type || 'deposit',
            title: resolved.title || 'Transaction',
            meta: resolved.country || '',
            user: resolved.user || 'Client',
            amount: resolved.amount || '',
            method: resolved.method || '',
            country: resolved.country || '',
            status: 'Transaction confirmed successfully',
            modalTitle: modalTitle,
            txnId: resolved.txnId || null,
            pair: resolved.pair || null,
            position: resolved.position || null,
            profit: resolved.profit || null,
            sector: resolved.sector || null,
            instrument: resolved.instrument || null
          };
        }
      }

      // Fallback: generate a basic notification procedurally
      if (window.PrimeBridge.random) {
        var r = PrimeBridge.random;
        var fallbackTypes = ['deposit', 'withdrawal', 'registration', 'bonus'];
        var fallbackMessages = [
          'Alex Johnson deposited funds',
          'New trader activated portfolio',
          'Withdrawal completed successfully',
          'Bonus unlocked on new account',
          'Managed portfolio updated',
          'New crypto trade executed',
          'User upgraded investment tier'
        ];
        var fallbackMetas = [
          '\uD83C\uDDFA\uD83C\uDDF8 USA',
          '\uD83C\uDDE8\uD83C\uDDE6 Canada',
          '\uD83C\uDDEC\uD83C\uDDE7 United Kingdom',
          '\uD83C\uDDE9\uD83C\uDDEA Germany',
          '\uD83C\uDDF3\uD83C\uDDEC Nigeria',
          '\uD83C\uDDE6\uD83C\uDDFA Australia'
        ];

        return {
          type: r.pickRandom(fallbackTypes),
          title: r.pickRandom(fallbackMessages),
          meta: r.pickRandom(fallbackMetas),
          modalTitle: 'Live Transaction',
          user: 'PrimeBridge Client',
          amount: '$' + r.randomInt(250, 9250).toLocaleString(),
          method: 'USDT / Bank Transfer',
          country: 'Verified Global Account',
          status: 'Transaction confirmed successfully'
        };
      }

      return null;
    },

    /**
     * Pick an event type using weighted probability.
     *
     * If an explicit eventTypes filter is set on the config, uses
     * uniform random selection from that filter. Otherwise selects
     * from the weighted distribution (WEIGHTED_TYPES) where types
     * with more entries are proportionally more likely.
     *
     * @private
     * @returns {string|undefined} Selected event type, or undefined
     *   if no types are available.
     */
    _getWeightedType: function () {
      if (Array.isArray(this._config.eventTypes)) {
        return PrimeBridge.random
          ? PrimeBridge.random.pickRandom(this._config.eventTypes)
          : this._config.eventTypes[Math.floor(Math.random() * this._config.eventTypes.length)];
      }

      return PrimeBridge.random
        ? PrimeBridge.random.pickWeighted(this.WEIGHTED_TYPES)
        : this.WEIGHTED_TYPES[Math.floor(Math.random() * this.WEIGHTED_TYPES.length)];
    },

    /**
     * Schedule the next orchestration tick with a random adaptive delay.
     *
     * @private
     * @returns {void}
     */
    _scheduleNextTick: function () {
      var self = this;

      var delay = PrimeBridge.random
        ? PrimeBridge.random.randomDelay(this._config.minDelay, this._config.maxDelay)
        : Math.floor(Math.random() * (this._config.maxDelay - this._config.minDelay + 1)) + this._config.minDelay;

      this._orchestrateTimerId = setTimeout(function () {
        self._orchestrateTick();
      }, delay);
    },

    /**
     * Stop the notification feed.
     *
     * In enhanced mode: does NOT stop the notifications.js auto-feed
     * (it was never touched). The engine simply stops any active
     * orchestration.
     *
     * In orchestrated mode: stops the adaptive loop and restores the
     * original state. The notifications.js feed does NOT restart
     * (call start() or PrimeBridge.notifications.startFeed() to resume).
     *
     * @returns {void}
     *
     * @example
     *   PrimeBridge.engine.stop();
     */
    stop: function () {
      var wasOrchestrated = this._intervalId !== null;

      this._clearOrchestration();

      if (wasOrchestrated) {
        this._log(
          '[PrimeBridge.engine] Orchestrated feed stopped. ' +
          'Total notifications fired this session: ' + this._totalFired
        );
      } else {
        this._log('[PrimeBridge.engine] Engine stopped (no active orchestration)');
      }
    },

    /**
     * Resume the feed after a stop().
     *
     * Reuses the last active configuration. If the engine was never
     * started, it calls start() with the default config.
     *
     * @returns {boolean} True if the feed was resumed
     *
     * @example
     *   PrimeBridge.engine.stop();
     *   // ... later ...
     *   PrimeBridge.engine.resume();
     */
    resume: function () {
      if (this._intervalId) {
        this._log('[PrimeBridge.engine] Already running, ignoring resume');
        return true;
      }

      return this.start();
    },

    /**
     * Clear all orchestration timers and marks.
     *
     * @private
     * @returns {void}
     */
    _clearOrchestration: function () {
      if (this._orchestrateTimerId) {
        clearTimeout(this._orchestrateTimerId);
        this._orchestrateTimerId = null;
      }
      this._intervalId = null;
    },

    /* ---------------------------------------------------------------- */
    /*  Status                                                            */
    /* ---------------------------------------------------------------- */

    /**
     * Check whether the engine's orchestrated feed is currently running.
     *
     * Note: In enhanced mode, the feed runs inside notifications.js;
     * this method returns false because the engine itself is not
     * doing its own timing loop. Use PrimeBridge.notifications.isRunning()
     * to check the underlying feed state in enhanced mode.
     *
     * @returns {boolean} True if the engine's own orchestration loop is active
     *
     * @example
     *   if (PrimeBridge.engine.isRunning()) { /* ... *\/ }
     */
    isRunning: function () {
      return this._intervalId !== null;
    },

    /**
     * Return a detailed status object describing the engine state.
     *
     * @returns {Object} Status snapshot
     * @property {boolean}  running       - Is the engine's orchestration active?
     * @property {string}   mode          - Current mode ('enhanced' | 'orchestrated')
     * @property {boolean}  eventsLoaded  - Was event data loaded successfully?
     * @property {number}   totalFired    - Total notifications fired by engine
     * @property {boolean}  notificationsRunning - Is the underlying feed running?
     * @property {number}   minDelay      - Current minimum delay (ms)
     * @property {number}   maxDelay      - Current maximum delay (ms)
     * @property {Array|null} eventTypes  - Active event type filter
     *
     * @example
     *   var status = PrimeBridge.engine.getStatus();
     *   console.log(status.mode, status.totalFired);
     */
    getStatus: function () {
      var notifRunning = false;
      if (
        window.PrimeBridge.notifications &&
        typeof PrimeBridge.notifications.isRunning === 'function'
      ) {
        notifRunning = PrimeBridge.notifications.isRunning();
      }

      return {
        running: this._intervalId !== null,
        mode: this._config.mode,
        eventsLoaded: this._eventsLoaded,
        totalFired: this._totalFired,
        notificationsRunning: notifRunning,
        minDelay: this._config.minDelay,
        maxDelay: this._config.maxDelay,
        eventTypes: this._config.eventTypes
      };
    },

    /* ---------------------------------------------------------------- */
    /*  Configuration Helpers                                             */
    /* ---------------------------------------------------------------- */

    /**
     * Update the engine configuration at runtime.
     *
     * Only affects future orchestration ticks. Does NOT change the
     * current running state (call stop() then start() to apply a mode
     * change).
     *
     * @param {Object} updates - Partial config object to merge
     * @returns {Object} The full current config after merge
     *
     * @example
     *   PrimeBridge.engine.setConfig({ debug: true, minDelay: 2000 });
     */
    setConfig: function (updates) {
      if (!updates || typeof updates !== 'object') return this._config;

      var validKeys = ['mode', 'minDelay', 'maxDelay', 'eventTypes', 'autoStart', 'debug'];

      for (var key in updates) {
        if (updates.hasOwnProperty(key) && validKeys.indexOf(key) !== -1) {
          this._config[key] = updates[key];
        }
      }

      this._log(
        '[PrimeBridge.engine] Config updated: ' + JSON.stringify(this._config)
      );

      return this._config;
    },

    /* ---------------------------------------------------------------- */
    /*  Internal Helpers                                                   */
    /* ---------------------------------------------------------------- */

    /**
     * Conditionally log a message to the console.
     *
     * @private
     * @param {string} msg - Message to log
     * @param {string} [level='log'] - Console method: 'log', 'warn', 'error'
     * @returns {void}
     */
    _log: function (msg, level) {
      if (!this._config.debug) return;

      level = level || 'log';

      switch (level) {
        case 'warn':
          console.warn(msg);
          break;
        case 'error':
          console.error(msg);
          break;
        default:
          console.log(msg);
      }
    },

    /**
     * Fire the onError callback if set.
     *
     * @private
     * @param {Error} err - The error to report
     * @returns {void}
     */
    _fireError: function (err) {
      if (typeof this.onError === 'function') {
        try {
          this.onError(err);
        } catch (cbErr) {
          // Prevent callback exceptions from cascading
          if (this._config.debug) {
            console.error('[PrimeBridge.engine] onError callback threw:', cbErr);
          }
        }
      }
    }
  };

  /* ------------------------------------------------------------------ */
  /*  Auto-Initialize on DOMContentLoaded                                 */
  /* ------------------------------------------------------------------ */

  /**
   * Boot the engine once the DOM is ready.
   *
   * Handles both the standard DOMContentLoaded event and the edge case
   * where the script loads after the event has already fired
   * (document.readyState === 'complete' or 'interactive').
   */
  function _boot() {
    PrimeBridge.engine.init().catch(function (err) {
      if (PrimeBridge.engine._config.debug) {
        console.error('[PrimeBridge.engine] Boot error:', err);
      }
    });
  }

  if (
    document.readyState === 'complete' ||
    document.readyState === 'interactive'
  ) {
    // DOM already ready — boot immediately
    _boot();
  } else {
    // Wait for DOMContentLoaded
    document.addEventListener('DOMContentLoaded', _boot);
  }

})();
