"use strict";

/* global TraceKit */
// js/vendor/tracekit.js

/**
 * general function, usually for data manipulation pages
 *
 */
var ErrorReport = {
  /**
   * @var object stores the last exception info
   */
  lastException: null,

  /**
   * handles thrown error exceptions based on user preferences
   *
   * @return void
   */
  errorHandler: function errorHandler(exception) {
    // issue: 14359
    if (JSON.stringify(ErrorReport.lastException) === JSON.stringify(exception)) {
      return;
    }

    if (exception.name === null || typeof exception.name === 'undefined') {
      exception.name = ErrorReport.extractExceptionName(exception);
    }

    ErrorReport.lastException = exception;
    $.post('index.php?route=/error-report', {
      'ajax_request': true,
      'server': CommonParams.get('server'),
      'get_settings': true,
      'exception_type': 'js'
    }, function (data) {
      if (data.success !== true) {
        Functions.ajaxShowMessage(data.error, false);
        return;
      }

      if (data.report_setting === 'ask') {
        ErrorReport.showErrorNotification();
      } else if (data.report_setting === 'always') {
        var reportData = ErrorReport.getReportData(exception);
        var postData = $.extend(reportData, {
          'send_error_report': true,
          'automatic': true
        });
        $.post('index.php?route=/error-report', postData, function (data) {
          if (data.success === false) {
            // in the case of an error, show the error message returned.
            Functions.ajaxShowMessage(data.error, false);
          } else {
            Functions.ajaxShowMessage(data.message, false);
          }
        });
      }
    });
  },

  /**
   * Shows the modal dialog previewing the report
   *
   * @param exception object error report info
   *
   * @return void
   */
  showReportDialog: function showReportDialog(exception) {
    var reportData = ErrorReport.getReportData(exception);
    /* Remove the hidden dialogs if there are*/

    if ($('#error_report_dialog').length !== 0) {
      $('#error_report_dialog').remove();
    }

    var $div = $('<div id="error_report_dialog"></div>');
    $div.css('z-index', '1000');
    var buttonOptions = {};

    buttonOptions[Messages.strSendErrorReport] = function () {
      var $dialog = $(this);
      var postData = $.extend(reportData, {
        'send_error_report': true,
        'description': $('#report_description').val(),
        'always_send': $('#always_send_checkbox')[0].checked
      });
      $.post('index.php?route=/error-report', postData, function (data) {
        $dialog.dialog('close');

        if (data.success === false) {
          // in the case of an error, show the error message returned.
          Functions.ajaxShowMessage(data.error, false);
        } else {
          Functions.ajaxShowMessage(data.message, 3000);
        }
      });
    };

    buttonOptions[Messages.strCancel] = function () {
      $(this).dialog('close');
    };

    $.post('index.php?route=/error-report', reportData, function (data) {
      if (data.success === false) {
        // in the case of an error, show the error message returned.
        Functions.ajaxShowMessage(data.error, false);
      } else {
        // Show dialog if the request was successful
        $div.append(data.message).dialog({
          title: Messages.strSubmitErrorReport,
          width: 650,
          modal: true,
          buttons: buttonOptions,
          close: function close() {
            $(this).remove();
          }
        });
      }
    });
  },

  /**
   * Shows the small notification that asks for user permission
   *
   * @return void
   */
  showErrorNotification: function showErrorNotification() {
    ErrorReport.removeErrorNotification();
    var $div = $('<div class="alert alert-danger userPermissionModal" role="alert" id="error_notification"></div>').append(Functions.getImage('s_error') + Messages.strErrorOccurred);
    var $buttons = $('<div class="floatright"></div>');
    var buttonHtml = '<button class="btn btn-primary" id="show_error_report">';
    buttonHtml += Messages.strShowReportDetails;
    buttonHtml += '</button>';
    buttonHtml += '<a id="change_error_settings">';
    buttonHtml += Functions.getImage('s_cog', Messages.strChangeReportSettings);
    buttonHtml += '</a>';
    buttonHtml += '<a href="#" id="ignore_error">';
    buttonHtml += Functions.getImage('b_close', Messages.strIgnore);
    buttonHtml += '</a>';
    $buttons.html(buttonHtml);
    $div.append($buttons); // eslint-disable-next-line compat/compat

    $div.appendTo(document.body);
    $(document).on('click', '#change_error_settings', ErrorReport.redirectToSettings);
    $(document).on('click', '#show_error_report', ErrorReport.createReportDialog);
    $(document).on('click', '#ignore_error', ErrorReport.removeErrorNotification);
  },

  /**
   * Removes the notification if it was displayed before
   *
   * @return void
   */
  removeErrorNotification: function removeErrorNotification(e) {
    if (e) {
      // don't remove the hash fragment by navigating to #
      e.preventDefault();
    }

    $('#error_notification').fadeOut(function () {
      $(this).remove();
    });
  },

  /**
   * Extracts Exception name from message if it exists
   *
   * @return String
   */
  extractExceptionName: function extractExceptionName(exception) {
    if (exception.message === null || typeof exception.message === 'undefined') {
      return '';
    }

    var reg = /([a-zA-Z]+):/;
    var regexResult = reg.exec(exception.message);

    if (regexResult && regexResult.length === 2) {
      return regexResult[1];
    }

    return '';
  },

  /**
   * Shows the modal dialog previewing the report
   *
   * @return void
   */
  createReportDialog: function createReportDialog() {
    ErrorReport.removeErrorNotification();
    ErrorReport.showReportDialog(ErrorReport.lastException);
  },

  /**
   * Redirects to the settings page containing error report
   * preferences
   *
   * @return void
   */
  redirectToSettings: function redirectToSettings() {
    window.location.href = 'index.php?route=/preferences/features';
  },

  /**
   * Returns the report data to send to the server
   *
   * @param exception object exception info
   *
   * @return object
   */
  getReportData: function getReportData(exception) {
    if (exception && exception.stack && exception.stack.length) {
      for (var i = 0; i < exception.stack.length; i++) {
        var stack = exception.stack[i];

        if (stack.context && stack.context.length) {
          for (var j = 0; j < stack.context.length; j++) {
            if (stack.context[j].length > 80) {
              stack.context[j] = stack.context[j].substring(-1, 75) + '//...';
            }
          }
        }
      }
    }

    var reportData = {
      'server': CommonParams.get('server'),
      'ajax_request': true,
      'exception': exception,
      'url': window.location.href,
      'exception_type': 'js'
    };

    if (AJAX.scriptHandler.scripts.length > 0) {
      reportData.scripts = AJAX.scriptHandler.scripts.map(function (script) {
        return script;
      });
    }

    return reportData;
  },

  /**
   * Wraps all global functions that start with PMA_
   *
   * @return void
   */
  wrapGlobalFunctions: function wrapGlobalFunctions() {
    for (var key in window) {
      if (key.indexOf('PMA_') === 0) {
        var global = window[key];

        if (typeof global === 'function') {
          window[key] = ErrorReport.wrapFunction(global);
        }
      }
    }
  },

  /**
   * Wraps given function in error reporting code and returns wrapped function
   *
   * @param func function to be wrapped
   *
   * @return function
   */
  wrapFunction: function wrapFunction(func) {
    if (!func.wrapped) {
      var newFunc = function newFunc() {
        try {
          return func.apply(this, arguments);
        } catch (x) {
          TraceKit.report(x);
        }
      };

      newFunc.wrapped = true; // Set guid of wrapped function same as original function, so it can be removed
      // See bug#4146 (problem with jquery draggable and sortable)

      newFunc.guid = func.guid = func.guid || newFunc.guid || jQuery.guid++;
      return newFunc;
    } else {
      return func;
    }
  },

  /**
   * Automatically wraps the callback in AJAX.registerOnload
   *
   * @return void
   */
  wrapAjaxOnloadCallback: function wrapAjaxOnloadCallback() {
    var oldOnload = AJAX.registerOnload;

    AJAX.registerOnload = function (file, func) {
      var wrappedFunction = ErrorReport.wrapFunction(func);
      oldOnload.call(this, file, wrappedFunction);
    };
  },

  /**
   * Automatically wraps the callback in $.fn.on
   *
   * @return void
   */
  wrapJqueryOnCallback: function wrapJqueryOnCallback() {
    var oldOn = $.fn.on;

    $.fn.on = function () {
      for (var i = 1; i <= 3; i++) {
        if (typeof arguments[i] === 'function') {
          arguments[i] = ErrorReport.wrapFunction(arguments[i]);
          break;
        }
      }

      return oldOn.apply(this, arguments);
    };
  },

  /**
   * Wraps all global functions that start with PMA_
   * also automatically wraps the callback in AJAX.registerOnload
   *
   * @return void
   */
  setUpErrorReporting: function setUpErrorReporting() {
    ErrorReport.wrapGlobalFunctions();
    ErrorReport.wrapAjaxOnloadCallback();
    ErrorReport.wrapJqueryOnCallback();
  }
};
AJAX.registerOnload('error_report.js', function () {
  TraceKit.report.subscribe(ErrorReport.errorHandler);
  ErrorReport.setUpErrorReporting();
  ErrorReport.wrapGlobalFunctions();
});