// jshint multistr: true
// jshint esversion: 6

let audit_url;
let ajax_url;

function getURLForType(type, event_data) {
  if (!event_data) {
    return;
  }
  switch (type) {
    case 'comment_create':
    case 'comment_update':
      if (event_data.job_id !== undefined) {
        return '/tests/' + event_data.job_id + '#comments';
      } else if (event_data.group_id !== undefined) {
        return '/group_overview/' + event_data.group_id + '#comments';
      } else if (event_data.parent_group_id !== undefined) {
        return '/parent_group_overview/' + event_data.parent_group_id + '#comments';
      }
      break;
    case 'jobtemplate_create':
      if (event_data.job_group_id) {
        return '/admin/job_templates/' + event_data.job_group_id;
      }
      break;
    case 'job_create':
    case 'job_update_result':
    case 'job_done':
    case 'job_restart':
      return '/tests/' + event_data.id;
    case 'jobgroup_create':
      if (event_data.id) {
        return '/group_overview/' + event_data.id;
      }
      break;
    case 'iso_create':
      return '/admin/productlog?id=' + event_data.scheduled_product_id;
    case 'table_create':
      if (event_data.id) {
        switch (event_data.table) {
          case 'Machines':
            return '/admin/machines?q=' + event_data.id;
          case 'Products':
            return '/admin/products?q=' + event_data.id;
          case 'TestSuites':
            return '/admin/test_suites?q=' + event_data.id;
        }
      }
      break;
    case 'worker_register':
      return urlWithBase('/admin/workers/' + event_data.id);
  }
}

function undoComments(undoButton) {
  const ids = undoButton.dataset.ids.split(',');
  if (!window.confirm(`Do you really want to delete the ${ids.length} comment(s)?`)) {
    return;
  }
  undoButton.style.display = 'none';
  const data = new FormData();
  for (const id of ids) {
    data.append('id', id);
  }
  fetchWithCSRF(urlWithBase('/api/v1/comments'), {method: 'DELETE', body: data})
    .then(response => {
      return response.json();
    })
    .then(response => {
      if (response.error) throw response.error;
      addFlash('info', 'The comments have been deleted.');
    })
    .catch(error => {
      console.error(error);
      undoButton.style.display = 'inline';
      addFlash('danger', `The comments could not be deleted: ${error}`);
    });
}

function getElementForEventType(type, eventData) {
  if (type !== 'comments_create') {
    return '';
  }
  const ids = (eventData?.created || [])
    .map(data => parseInt(data?.id))
    .filter(Number.isInteger)
    .join(',');
  return `<br><button class="btn btn-danger undo-event" style="float: right" data-ids="${ids}" onclick="undoComments(this)">Undo</button>`;
}

function loadAuditLogTable() {
  $('#audit_log_table').DataTable({
    lengthMenu: [20, 40, 100],
    processing: true,
    serverSide: true,
    search: {search: searchquery},
    ajax: {url: ajax_url, type: 'GET', dataType: 'json'},
    columns: [
      {data: 'id'},
      {data: 'event_time'},
      {data: 'user'},
      {data: 'connection'},
      {data: 'event'},
      {data: 'event_data'}
    ],
    order: [[0, 'desc']],
    columnDefs: [
      {targets: 0, visible: false},
      {
        targets: 1,
        render: function (data, type, row) {
          if (type === 'display')
            // I want to have a link to events for cases when one wants to share interesting event
            return (
              '<a href="' +
              audit_url +
              '?eventid=' +
              row.id +
              '" title=' +
              data +
              '>' +
              timeago.format(data + 'Z') +
              '</a>'
            );
          else return data;
        }
      },
      {targets: 3, visible: false},
      {
        targets: 4,
        render: function (data, type, row) {
          if (type === 'display') {
            // Look for an id, and if we have one match it with an event type
            try {
              const url = urlWithBase(getURLForType(row.event, JSON.parse(row.event_data)));
              if (url) {
                return '<a class="audit_event_details" href="' + url + '">' + htmlEscape(data) + '</a>';
              }
            } catch (e) {
              // Intentionally ignore all errors
            }
          }
          return data;
        }
      },
      {
        targets: 5,
        width: '70%',
        render: function (data, type, row) {
          if (type === 'display') {
            if (!data || data === 'null') {
              return '';
            }
            let parsedData;
            let typeSpecificElement = '';
            try {
              const eventData = JSON.parse(data);
              parsedData = JSON.stringify(eventData, null, 2);
              typeSpecificElement = getElementForEventType(row.event, eventData);
            } catch (e) {
              parsedData = data;
            }
            const escapedData = htmlEscape(parsedData);
            return `${typeSpecificElement}<span class="audit_event_data" title="${escapedData}">${escapedData}</span>`;
          } else {
            return data;
          }
        }
      }
    ]
  });
}

let scheduledProductsTable;

function dataForLink(link) {
  const rowData = scheduledProductsTable.row(link.parentElement?.parentElement).data();
  if (rowData === undefined) {
    console.error('unable to find row data for action link');
  }
  return rowData;
}

function showScheduledProductModalDialog(title, body) {
  const modalElement = document.getElementById('scheduled-product-modal');
  const modalDialog = new bootstrap.Modal(modalElement);
  modalElement.getElementsByClassName('modal-title')[0].textContent = title;
  modalElement.getElementsByClassName('modal-body')[0].replaceChildren(body);
  modalDialog.show();
}

function renderScheduledProductSettings(settings) {
  const tbody = document.createElement('tbody');
  for (const [key, value] of Object.entries(settings || {})) {
    const tr = document.createElement('tr');
    const keyTd = document.createElement('td');
    const valueTd = document.createElement('td');
    keyTd.append(key);
    valueTd.style.whiteSpace = 'pre-wrap';
    valueTd.append(renderHttpUrlAsLink(value));
    tr.append(keyTd, valueTd);
    tbody.append(tr);
  }
  const table = document.createElement('table');
  table.className = 'table table-striped settings-table';
  table.append(tbody);
  return table;
}

function showScheduledProductSettings(link) {
  const rowData = dataForLink(link);
  if (rowData !== undefined) {
    showScheduledProductModalDialog('Scheduled product settings', renderScheduledProductSettings(rowData.settings));
  }
}

function renderFailedJobInfo(results) {
  const failedJobInfo = results.failed_job_info;
  delete results.failed_job_info;
  if (!Array.isArray(failedJobInfo) || failedJobInfo.length === 0) {
    return [];
  }
  const heading = createElement('h4', 'Errors occurred when scheduling this product');
  const failedElements = createElement(
    'ul',
    failedJobInfo.map(info => {
      return createElement('li', [renderJobLink(info.job_id), ': ', renderMessages(info.error_messages)]);
    })
  );
  return [createElement('div', [heading, failedElements], {class: 'alert alert-danger'})];
}

function renderSuccessfullyScheduledJobs(results) {
  const successfulJobIDs = results.successful_job_ids;
  delete results.successful_job_ids;
  if (!Array.isArray(successfulJobIDs) || successfulJobIDs.length === 0) {
    return [];
  }
  const heading = createElement('h4', `Successfully scheduled ${successfulJobIDs.length} job(s)`);
  const jobLinks = successfulJobIDs.flatMap(id => [renderJobLink(id), ', ']);
  if (jobLinks.length > 1) {
    jobLinks.pop();
  }
  return [createElement('div', [heading, ...jobLinks], {class: 'alert alert-success'})];
}

function renderNotes(results, property, type = 'info') {
  const note = results[property];
  delete results[property];
  return note !== undefined ? [createElement('div', [renderMessages(note)], {class: `alert alert-${type}`})] : [];
}

function renderUnknownResults(results) {
  return Object.keys(results).length > 0 ? [createElement('pre', JSON.stringify(results, undefined, 4))] : [];
}

function renderScheduledProductResults(results) {
  if (results === null || typeof results !== 'object') {
    return createElement('p', 'No results available.');
  }
  delete results.error_code; // not meaningful to users and results.error is enough
  return createElement('div', [
    ...renderNotes(results, 'note'), // notes added by external tools via API
    ...renderNotes(results, 'error', 'danger'), // error message in case scheduling was not possible at all
    ...renderFailedJobInfo(results), // error messages about failed jobs in case the overall scheduling succeeded
    ...renderSuccessfullyScheduledJobs(results), // IDs of successfully scheduled jobs
    ...renderNotes(results, 'notes'), // notes added by openQA itself under certain conditions
    ...renderUnknownResults(results) // everything not covered by the above
  ]);
}

function showScheduledProductResults(link) {
  const rowData = dataForLink(link);
  if (rowData !== undefined) {
    showScheduledProductModalDialog(
      'Scheduled product results',
      renderScheduledProductResults(Object.create(rowData.results))
    );
  }
}

function rescheduleProductForActionLink(link) {
  const id = dataForLink(link)?.id;
  if (id && window.confirm('Do you really want to reschedule all jobs for the product ' + id + '?')) {
    rescheduleProduct(scheduledProductsTable.rescheduleUrlTemplate.replace('XXXXX', id));
  }
}

function rescheduleProduct(url) {
  $.post({
    url: url,
    success: (data, textStatus, jqXHR) => {
      const id = jqXHR.responseJSON?.scheduled_product_id;
      const msg =
        typeof id === 'number'
          ? 'The product has been re-triggered as <a href="/admin/productlog?id=' + id + '">' + id + '</a>.'
          : 'Re-scheduling the product has been triggered.';
      addFlash('info', msg);
    },
    error: (jqXHR, textStatus, errorThrown) => {
      addFlash('danger', 'Unable to trigger re-scheduling: ' + getXhrError(jqXHR, textStatus, errorThrown));
    }
  });
}

function showSettingsAndResults(rowData) {
  const scheduledProductsDiv = $('#scheduled-products');
  scheduledProductsDiv.append($('<h3>Results</h3>'));
  scheduledProductsDiv.append(renderScheduledProductResults(rowData.results));
  scheduledProductsDiv.append($('<h3>Settings</h3>'));
  scheduledProductsDiv.append(renderScheduledProductSettings(rowData.settings));
}

function loadProductLogTable(dataTableUrl, rescheduleUrlTemplate, showActions) {
  const params = new URLSearchParams(document.location.search.substring(1));
  const id = params.get('id');
  let settingsAndResultsShown = false;
  if (id) {
    dataTableUrl += '?id=' + encodeURIComponent(id);
    $('#scheduled-products h2').text('Scheduled product ' + id);
  }

  scheduledProductsTable = $('#product_log_table').DataTable({
    lengthMenu: [10, 25, 50],
    processing: true,
    serverSide: true,
    order: [[1, 'desc']],
    ajax: {
      url: dataTableUrl,
      type: 'GET',
      dataType: 'json',
      dataSrc: function (json) {
        const data = json.data;
        if (id && !settingsAndResultsShown) {
          showSettingsAndResults(data[0]);
          settingsAndResultsShown = true;
        }
        return data;
      }
    },
    columns: [
      {data: 'id'},
      {data: 't_created'},
      {data: 'user_name'},
      {data: 'status'},
      {data: 'distri'},
      {data: 'version'},
      {data: 'flavor'},
      {data: 'arch'},
      {data: 'build'},
      {data: 'iso'},
      {data: 'id'}
    ],
    columnDefs: [
      {
        targets: 0,
        visible: !id,
        render: function (data, type, row) {
          return type === 'display' ? '<a href="?id=' + encodeURIComponent(data) + '">' + data + '</a>' : data;
        }
      },
      {
        targets: 1,
        render: function (data, type, row) {
          return type === 'display' ? timeago.format(data + 'Z') : data;
        }
      },
      {targets: 2, orderable: false},
      {
        targets: 6,
        render: function (data, type, row) {
          return (
            "<a href='" +
            urlWithBase(
              '/tests/overview?build=' +
                encodeURIComponent(row.build) +
                '&distri=' +
                encodeURIComponent(row.distri) +
                '&version=' +
                encodeURIComponent(row.version) +
                '&flavor=' +
                encodeURIComponent(data)
            ) +
            "'>" +
            data +
            '</a>'
          );
        }
      },
      {
        targets: 8,
        render: function (data, type, row) {
          return (
            "<a href='" +
            urlWithBase(
              '/tests/overview?build=' +
                encodeURIComponent(data) +
                '&distri=' +
                encodeURIComponent(row.distri) +
                '&version=' +
                encodeURIComponent(row.version)
            ) +
            "'>" +
            data +
            '</a>'
          );
        }
      },
      {
        targets: 10,
        orderable: false,
        render: function (data, type, row) {
          let html = '';
          if (!id) {
            html +=
              '<a href="#" onclick="showScheduledProductSettings(this); return true;">\
                                 <i class="action fa-solid fa-search-plus" title="Show settings"></i></a>\
                                 <a href="#" onclick="showScheduledProductResults(this); return true;">\
                                 <i class="action fa-solid fa-file" title="Show results"></i></a>';
          }
          if (showActions) {
            html +=
              '<a href="#" onclick="rescheduleProductForActionLink(this); return true;">\
                                 <i class="action fa-solid fa-rotate-left" title="Reschedule product tests"></i></a>';
          }
          return html;
        }
      }
    ]
  });

  scheduledProductsTable.rescheduleUrlTemplate = rescheduleUrlTemplate;

  // remove unneccassary elements when showing only one particular product
  if (id) {
    const wrapper = document.getElementById('product_log_table_wrapper');
    wrapper.removeChild(wrapper.firstChild);
    wrapper.removeChild(wrapper.lastChild);
  }
}
