// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('extensions', function() {
  'use strict';

  /** @interface */
  class ActivityLogEventDelegate {
    /** @return {!ChromeEvent} */
    getOnExtensionActivity() {}
  }

  /**
   * Process activity for the stream. In the case of content scripts, we split
   * the activity for every script invoked.
   * @param {!chrome.activityLogPrivate.ExtensionActivity}
   *     activity
   * @return {!Array<!extensions.StreamItem>}
   */
  function processActivityForStream(activity) {
    const activityType = activity.activityType;
    const timestamp = activity.time;
    const isContentScript = activityType ===
        chrome.activityLogPrivate.ExtensionActivityType.CONTENT_SCRIPT;

    const args = isContentScript ? JSON.stringify([]) : activity.args;

    let streamItemNames = [activity.apiCall];

    // TODO(kelvinjiang): Reuse logic from activity_log_history and refactor
    // some of the processing code into a separate file in a follow up CL.
    if (isContentScript) {
      streamItemNames = activity.args ? JSON.parse(activity.args) : [];
      assert(Array.isArray(streamItemNames), 'Invalid data for script names.');
    }

    const other = activity.other;
    const webRequestInfo = other && other.webRequest;

    return streamItemNames.map(name => ({
                                 args,
                                 argUrl: activity.argUrl,
                                 activityType,
                                 name,
                                 pageUrl: activity.pageUrl,
                                 timestamp,
                                 webRequestInfo,
                                 expanded: false,
                               }));
  }

  const ActivityLogStream = Polymer({
    is: 'activity-log-stream',

    properties: {
      /** @type {string} */
      extensionId: String,

      /** @type {!extensions.ActivityLogEventDelegate} */
      delegate: Object,

      /** @private */
      isStreamOn_: {
        type: Boolean,
        value: false,
      },

      /** @private {!Array<!extensions.StreamItem>} */
      activityStream_: {
        type: Array,
        value: () => [],
      },

      /** @private {!Array<!extensions.StreamItem>} */
      filteredActivityStream_: {
        type: Array,
        computed:
            'computeFilteredActivityStream_(activityStream_.*, lastSearch_)',
      },

      /** @private */
      lastSearch_: {
        type: String,
        value: '',
      },
    },

    listeners: {
      'resize-stream': 'onResizeStream_',
    },

    /**
     * Instance of |extensionActivityListener_| bound to |this|.
     * @private {!Function}
     */
    listenerInstance_: () => {},

    /** @override */
    attached: function() {
      // Since this component is not restamped, this will only be called once
      // in its lifecycle.
      this.listenerInstance_ = this.extensionActivityListener_.bind(this);
      this.startStream();
    },

    /** @private */
    onResizeStream_: function(e) {
      this.$$('iron-list').notifyResize();
    },

    clearStream: function() {
      this.splice('activityStream_', 0, this.activityStream_.length);
    },

    startStream: function() {
      if (this.isStreamOn_) {
        return;
      }

      this.isStreamOn_ = true;
      this.delegate.getOnExtensionActivity().addListener(
          this.listenerInstance_);
    },

    pauseStream: function() {
      if (!this.isStreamOn_) {
        return;
      }

      this.delegate.getOnExtensionActivity().removeListener(
          this.listenerInstance_);
      this.isStreamOn_ = false;
    },

    /** @private */
    onToggleButtonClick_: function() {
      if (this.isStreamOn_) {
        this.pauseStream();
      } else {
        this.startStream();
      }
    },

    /**
     * @private
     * @return {boolean}
     */
    isStreamEmpty_: function() {
      return this.activityStream_.length == 0;
    },

    /**
     * @private
     * @return {boolean}
     */
    isFilteredStreamEmpty_: function() {
      return this.filteredActivityStream_.length == 0;
    },

    /**
     * @private
     * @return {boolean}
     */
    shouldShowEmptySearchMessage_: function() {
      return !this.isStreamEmpty_() && this.isFilteredStreamEmpty_();
    },

    /**
     * @private
     * @param {!chrome.activityLogPrivate.ExtensionActivity} activity
     */
    extensionActivityListener_: function(activity) {
      if (activity.extensionId != this.extensionId) {
        return;
      }

      this.splice(
          'activityStream_', this.activityStream_.length, 0,
          ...processActivityForStream(activity));

      // Used to update the scrollbar.
      this.$$('iron-list').notifyResize();
    },

    /**
     * @private
     * @param {!CustomEvent<string>} e
     */
    onSearchChanged_: function(e) {
      // Remove all whitespaces from the search term, as API call names and
      // URLs should not contain any whitespace. As of now, only single term
      // search queries are allowed.
      const searchTerm = e.detail.replace(/\s+/g, '').toLowerCase();
      if (searchTerm === this.lastSearch_) {
        return;
      }

      this.lastSearch_ = searchTerm;
    },

    /**
     * @private
     * @return {!Array<!extensions.StreamItem>}
     */
    computeFilteredActivityStream_: function() {
      if (!this.lastSearch_) {
        return this.activityStream_.slice();
      }

      // Match on these properties for each activity.
      const propNames = [
        'name',
        'pageUrl',
        'activityType',
      ];

      return this.activityStream_.filter(act => {
        return propNames.some(prop => {
          return act[prop] &&
              act[prop].toLowerCase().includes(this.lastSearch_);
        });
      });
    },
  });

  return {
    ActivityLogStream: ActivityLogStream,
    ActivityLogEventDelegate: ActivityLogEventDelegate,
  };
});
