import find from 'lodash/find';

function escapeDangerousCSVCharacters(data) {
  if (typeof data === 'string') {
    // Places single quote before the appearance of dangerous characters if they
    // are the first in the data string.
    // eslint-disable-next-line no-useless-escape
    return data.replace(/^\+|^\-|^\=|^\@/g, "'$&");
  }

  return data;
}

function buildCSV(columns, data, options) {
  const replaceDoubleQuoteInString = (columnData) =>
    typeof columnData === 'string' ? columnData.replace(/"/g, '""') : columnData;

  // eslint-disable-next-line no-shadow
  const buildHead = (tmpColumns) => {
    return `${tmpColumns
      .reduce(
        (soFar, column) =>
          column.download
            ? `${soFar}"${escapeDangerousCSVCharacters(replaceDoubleQuoteInString(column.label || column.name))}"${
                options.downloadOptions.separator
              }`
            : soFar,
        ''
      )
      .slice(0, -1)}\r\n`;
  };

  const CSVHead = buildHead(columns);

  // eslint-disable-next-line no-shadow
  const buildBody = (tmpData) => {
    if (!tmpData.length) return '';
    return tmpData
      .reduce(
        (soFar, row) =>
          `${soFar}"${row.data
            .filter((_, index) => columns[index].download)
            .map((columnData) => escapeDangerousCSVCharacters(replaceDoubleQuoteInString(columnData)))
            .join(`"${options.downloadOptions.separator}"`)}"\r\n`,
        ''
      )
      .trim();
  };
  const CSVBody = buildBody(data);

  const csv = options.onDownload
    ? options.onDownload(buildHead, buildBody, columns, data)
    : `${CSVHead}${CSVBody}`.trim();

  return csv;
}

function downloadCSV(csv, filename) {
  const blob = new Blob([csv], { type: 'text/csv' });

  /* taken from react-csv */
  if (navigator && navigator.msSaveOrOpenBlob) {
    navigator.msSaveOrOpenBlob(blob, filename);
  } else {
    const dataURI = `data:text/csv;charset=utf-8,${csv}`;

    const URL = window.URL || window.webkitURL;
    const downloadURI = typeof URL.createObjectURL === 'undefined' ? dataURI : URL.createObjectURL(blob);

    const link = document.createElement('a');
    link.setAttribute('href', downloadURI);
    link.setAttribute('download', filename);
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }
}

// eslint-disable-next-line no-shadow
function createCSVDownload(columns, data, options, tmpDownloadCSV) {
  const csv = buildCSV(columns, data, options);

  if (options.onDownload && csv === false) {
    return;
  }

  tmpDownloadCSV(csv, options.downloadOptions.filename);
}

export function handleCSVDownload(props) {
  const { data, displayData, columns, options, columnOrder } = props;
  let dataToDownload = [];
  let columnsToDownload = [];
  let columnOrderCopy = Array.isArray(columnOrder) ? columnOrder.slice(0) : [];

  if (columnOrderCopy.length === 0) {
    columnOrderCopy = columns.map((item, idx) => idx);
  }

  data.forEach((row) => {
    const newRow = { index: row.index, data: [] };
    columnOrderCopy.forEach((idx) => {
      newRow.data.push(row.data[idx]);
    });
    dataToDownload.push(newRow);
  });

  columnOrderCopy.forEach((idx) => {
    columnsToDownload.push(columns[idx]);
  });

  if (options.downloadOptions && options.downloadOptions.filterOptions) {
    // check rows first:
    if (options.downloadOptions.filterOptions.useDisplayedRowsOnly) {
      const filteredDataToDownload = displayData.map((row, index) => {
        let i = -1;

        // Help to preserve sort order in custom render columns
        row.index = index;

        return {
          data: row.data.map((column) => {
            i += 1;

            // if we have a custom render, which will appear as a react element, we must grab the actual value from data
            // that matches the dataIndex and column
            // TODO: Create a utility function for checking whether or not something is a react object
            let val =
              typeof column === 'object' && column !== null && !Array.isArray(column)
                ? find(data, (d) => d.index === row.dataIndex).data[i]
                : column;
            val = typeof val === 'function' ? find(data, (d) => d.index === row.dataIndex).data[i] : val;
            return val;
          }),
        };
      });

      dataToDownload = [];
      filteredDataToDownload.forEach((row) => {
        const newRow = { index: row.index, data: [] };
        columnOrderCopy.forEach((idx) => {
          newRow.data.push(row.data[idx]);
        });
        dataToDownload.push(newRow);
      });
    }

    // now, check columns:
    if (options.downloadOptions.filterOptions.useDisplayedColumnsOnly) {
      columnsToDownload = columnsToDownload.filter((_) => _.display === 'true');

      dataToDownload = dataToDownload.map((row) => {
        row.data = row.data.filter((_, index) => columns[columnOrderCopy[index]].display === 'true');
        return row;
      });
    }
  }
  createCSVDownload(columnsToDownload, dataToDownload, options, downloadCSV);
}

export default { handleCSVDownload };
