"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Experience = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _analytics = require("../analytics");
var _consts = require("./consts");
var _experienceState = require("./experience-state");
var _ExperienceCheckComposite = require("./ExperienceCheckComposite");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var Experience = exports.Experience = /*#__PURE__*/function () {
  /**
   * Indicates whether sampled tracking is enabled for this current experience session.
   *
   * Set to true | false upon transitioning to 'started' state.
   * When true, on subsequent transitions we fire experienceSampled events.
   * Ensures that every tracked start has corresponding abort/fail/success tracked.
   */

  /**
   * Timestamp (in milliseconds) when the experience transitioned to 'started' state.
   *
   * Used to calculate experience duration. Set via Date.now() when experience starts.
   */

  /**
   * Metadata provided at experience start time to merge into subsequent events.
   *
   * Used to retain experienceStartMethod and other start-specific metadata.
   */

  /**
   * Creates a new Experience instance for tracking user experiences.
   *
   * @param id - Unique identifier for the experience e.g. 'toolbarOpen' 'menuAction'
   * @param options - Configuration options for the experience
   * @param options.checks - Experience checks to monitor for completion
   * @param options.dispatchAnalyticsEvent - Function to dispatch analytics events
   * @param options.sampleRate - Sample rate for experienceSampled events
   * @param options.metadata - Global metadata to attach to all events
   * @param options.action - Optional sub identifier for the specific experience action e.g. 'bold' 'insertTable'
   * @param options.actionSubjectId - Optional sub identifier for the experience action subject e.g. 'selectionToolbar' 'quickInsert'
   */
  function Experience(id, options) {
    var _options$sampleRate;
    (0, _classCallCheck2.default)(this, Experience);
    (0, _defineProperty2.default)(this, "currentState", 'pending');
    /**
     * Set of experience states that have been seen in the current session.
     *
     * Used to determine if experienceStatusFirstSeen flag should be set in events.
     */
    (0, _defineProperty2.default)(this, "statesSeen", new Set());
    this.id = id;
    this.actionSubjectId = options.actionSubjectId;
    this.dispatchAnalyticsEvent = options.dispatchAnalyticsEvent;
    this.sampleRate = (_options$sampleRate = options.sampleRate) !== null && _options$sampleRate !== void 0 ? _options$sampleRate : _consts.DEFAULT_EXPERIENCE_SAMPLE_RATE;
    this.globalMetadata = _objectSpread(_objectSpread({}, options.metadata), {}, {
      experienceAction: options.action
    });
    this.check = new _ExperienceCheckComposite.ExperienceCheckComposite(options.checks || []);
  }
  return (0, _createClass2.default)(Experience, [{
    key: "startCheck",
    value: function startCheck() {
      var _this = this;
      this.stopCheck();
      this.check.start(function (_ref) {
        var status = _ref.status,
          reason = _ref.reason,
          metadata = _ref.metadata;
        if (status === 'success') {
          _this.success({
            reason: reason,
            metadata: metadata
          });
        } else if (status === 'abort') {
          _this.abort({
            reason: reason,
            metadata: metadata
          });
        } else if (status === 'failure') {
          _this.failure({
            reason: reason,
            metadata: metadata
          });
        }
      });
    }
  }, {
    key: "stopCheck",
    value: function stopCheck() {
      this.check.stop();
    }
  }, {
    key: "getEndStateMetadata",
    value: function getEndStateMetadata() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var metadata = _objectSpread({}, options.metadata);
      if (options.reason) {
        metadata.experienceEndReason = options.reason;
      }
      return metadata;
    }

    /**
     * Transitions to a new experience state and tracks analytics events.
     *
     * Upon transition to each state, two events are tracked:
     * - experienceMeasured: tracked on every successful transition, used for data analysis
     * - experienceSampled: tracked only on 1 out of every N transitions based on sample rate
     *
     * @param toState - The target state to transition to
     * @param metadata - Optional metadata to attach to the analytics events
     * @returns true if transition was successful, false if invalid transition
     */
  }, {
    key: "transitionTo",
    value: function transitionTo(toState, metadata) {
      if (!(0, _experienceState.canTransition)(this.currentState, toState)) {
        return false;
      }
      this.currentState = toState;
      if (toState === 'started') {
        this.isSampledTrackingEnabled = Math.random() < this.sampleRate;
        this.startTimeMs = Date.now();
        this.startMetadata = metadata;
      }
      this.trackTransition(toState, metadata);
      if (toState === 'started') {
        this.startCheck();
      } else {
        this.stopCheck();
        this.startMetadata = undefined;
      }
      this.statesSeen.add(toState);
      return true;
    }

    /**
     * Tracks analytics events for a state transition.
     *
     * Fires both experienceMeasured (always) and experienceSampled (sampled) events.
     *
     * @param toState - The state that was transitioned to
     * @param metadata - Metadata to include in the event, including firstInSession flag
     */
  }, {
    key: "trackTransition",
    value: function trackTransition(toState, metadata) {
      var attributes = _objectSpread(_objectSpread(_objectSpread({
        experienceKey: this.id,
        experienceStatus: toState,
        experienceStatusFirstSeen: !this.statesSeen.has(toState),
        experienceStartTime: this.startTimeMs || 0,
        experienceDuration: this.startTimeMs ? Date.now() - this.startTimeMs : 0
      }, this.globalMetadata), this.startMetadata), metadata);
      var experienceMeasuredEvent = {
        action: _analytics.ACTION.EXPERIENCE_MEASURED,
        actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
        actionSubjectId: this.actionSubjectId,
        eventType: _analytics.EVENT_TYPE.OPERATIONAL,
        attributes: attributes
      };
      this.dispatchAnalyticsEvent(experienceMeasuredEvent);
      if (this.isSampledTrackingEnabled) {
        var experienceSampledEvent = {
          action: _analytics.ACTION.EXPERIENCE_SAMPLED,
          actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
          actionSubjectId: this.actionSubjectId,
          eventType: _analytics.EVENT_TYPE.OPERATIONAL,
          attributes: attributes
        };
        this.dispatchAnalyticsEvent(experienceSampledEvent);
      }
    }

    /**
     * Starts tracking the experience and all checks which monitor for completion.
     *
     * Metadata from options will be merged with metadata provided in subsequent events.
     *
     * @param options - Configuration for starting the experience
     * @param options.metadata - Optional custom metadata attached to all subsequent events for this started experience
     * @param options.forceRestart - If true and experience already in progress will abort and restart
     * @param options.method - Optional method for experience start, e.g., how the experience was initiated
     */
  }, {
    key: "start",
    value: function start() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      if (options.forceRestart && this.currentState === 'started') {
        this.abort({
          reason: _consts.EXPERIENCE_ABORT_REASON.RESTARTED
        });
      }
      return this.transitionTo('started', _objectSpread({
        experienceStartMethod: options.method
      }, options.metadata));
    }

    /**
     * Marks the experience as successful and stops any ongoing checks.
     *
     * @param options - Configuration for the success event
     * @param options.metadata - Optional custom metadata attached to the success event
     * @param options.reason - Optional reason for success
     * @returns false if transition to success state was not valid
     */
  }, {
    key: "success",
    value: function success() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var metadata = this.getEndStateMetadata(options);
      return this.transitionTo('succeeded', metadata);
    }

    /**
     * Aborts the experience and stops any ongoing checks.
     *
     * Use this when a started experience terminates early due to user action or context change
     * (e.g., component unmount, navigation). This is neither success nor failure.
     *
     * @param options - Configuration for the abort event
     * @param options.metadata - Optional custom metadata attached to the abort event
     * @param options.reason - Optional reason for abort
     *
     * @example
     * // Abort on component unmount
     * useEffect(() => {
     *   return () => experience.abort({ reason: 'unmount', metadata: { someKey: 'someValue' } });
     * }, []);
     *
     * @returns false if transition to aborted state was not valid
     */
  }, {
    key: "abort",
    value: function abort() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var metadata = this.getEndStateMetadata(options);
      return this.transitionTo('aborted', metadata);
    }

    /**
     * Manually marks the experience as failed and stops any ongoing checks.
     *
     * Use this for actual failures in the experience flow (e.g., timeout, error conditions).
     *
     * @param options - Configuration for the failure event
     * @param options.metadata - Optional custom metadata attached to the failure event
     * @param options.reason - Optional reason for failure
     * @returns false if transition to failed state was not valid
     */
  }, {
    key: "failure",
    value: function failure() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var metadata = this.getEndStateMetadata(options);
      return this.transitionTo('failed', metadata);
    }
  }]);
}();