<template>
  <div>
    <v-menu open-on-hover bottom offset-y transition="slide-y-transition">
      <template #activator="{ on }">
        <v-btn
          v-t="['c:actions']"
          outlined
          class="nc-actions-menu-btn caption px-2 nc-remove-border font-weight-medium"
          small
          text
          v-on="on"
        >
          <v-icon small color="#777"> mdi-flash-outline </v-icon>
          <!--More-->
          {{ $t('general.more') }}

          <v-icon small color="#777"> mdi-menu-down </v-icon>
        </v-btn>
      </template>

      <v-list dense>
        <v-list-item v-t="['a:actions:download-csv']" dense @click="exportCsv">
          <v-list-item-title>
            <v-icon small class="mr-1"> mdi-download-outline </v-icon>
            <span class="caption">
              <!-- Download as CSV -->
              {{ $t('activity.downloadCSV') }}
            </span>
          </v-list-item-title>
        </v-list-item>
        <v-list-item v-t="['a:actions:download-excel']" dense @click="exportExcel">
          <v-list-item-title>
            <v-icon small class="mr-1"> mdi-download-outline </v-icon>
            <span class="caption">
              <!-- Download as XLSX -->
              {{ $t('activity.downloadExcel') }}
            </span>
          </v-list-item-title>
        </v-list-item>
        <v-list-item
          v-if="_isUIAllowed('csvImport') && !isView"
          v-t="['a:actions:upload-csv']"
          dense
          @click="importModal = true"
        >
          <v-list-item-title>
            <v-icon small class="mr-1" color=""> mdi-upload-outline </v-icon>
            <span class="caption">
              <!-- Upload CSV -->
              {{ $t('activity.uploadCSV') }}
            </span>

            <span class="caption grey--text">(<x-icon small color="grey lighten-2"> mdi-alpha </x-icon> version)</span>
          </v-list-item-title>
        </v-list-item>
        <v-list-item
          v-if="_isUIAllowed('SharedViewList') && !isView"
          v-t="['a:actions:shared-view-list']"
          dense
          @click="$emit('showAdditionalFeatOverlay', 'shared-views')"
        >
          <v-list-item-title>
            <v-icon small class="mr-1" color=""> mdi-view-list-outline </v-icon>
            <span class="caption">
              <!-- Shared View List -->
              {{ $t('activity.listSharedView') }}
            </span>
          </v-list-item-title>
        </v-list-item>
        <v-list-item
          v-if="_isUIAllowed('webhook') && !isView"
          v-t="['c:actions:webhook']"
          dense
          @click="webhookModal = true"
        >
          <v-list-item-title>
            <v-icon small class="mr-1" color=""> mdi-hook </v-icon>
            <span class="caption"> Webhooks </span>
          </v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
    <drop-or-select-file-modal v-model="importModal" accept=".csv" text="CSV" @file="onCsvFileSelection" />
    <column-mapping-modal
      v-if="columnMappingModal && meta"
      v-model="columnMappingModal"
      :meta="meta"
      :import-data-columns="parsedCsv.columns"
      :parsed-csv="parsedCsv"
      @import="importData"
    />
    <!--    <webhook-modal v-if="webhookModal" v-model="webhookModal" :meta="meta" />-->
    <webhook-slider v-model="webhookModal" :meta="meta" />
  </div>
</template>

<script>
import FileSaver from 'file-saver';
import * as XLSX from 'xlsx';
import { ExportTypes } from 'nocodb-sdk';
import DropOrSelectFileModal from '~/components/import/DropOrSelectFileModal';
import ColumnMappingModal from '~/components/project/spreadsheet/components/importExport/ColumnMappingModal';
import CSVTemplateAdapter from '~/components/import/templateParsers/CSVTemplateAdapter';
import { UITypes } from '~/components/project/spreadsheet/helpers/uiTypes';
import WebhookModal from '~/components/project/tableTabs/webhook/WebhookModal';
import WebhookSlider from '~/components/project/tableTabs/webhook/WebhookSlider';

export default {
  name: 'ExportImport',
  components: {
    WebhookSlider,
    WebhookModal,
    ColumnMappingModal,
    DropOrSelectFileModal,
  },
  props: {
    meta: Object,
    nodes: Object,
    selectedView: Object,
    publicViewId: String,
    queryParams: Object,
    isView: Boolean,
    reqPayload: Object,
  },
  data() {
    return {
      importModal: false,
      columnMappingModal: false,
      parsedCsv: {},
      webhookModal: false,
    };
  },

  methods: {
    async onCsvFileSelection(file) {
      const reader = new FileReader();
      reader.onload = async e => {
        const templateGenerator = new CSVTemplateAdapter(file.name, e.target.result);
        await templateGenerator.init();
        templateGenerator.parseData();
        this.parsedCsv.columns = templateGenerator.getColumns();
        this.parsedCsv.data = templateGenerator.getData();
        this.columnMappingModal = true;
        this.importModal = false;
      };

      reader.readAsText(file);
    },

    async extractCsvData() {
      return Promise.all(
        this.data.map(async r => {
          const row = {};
          for (const col of this.availableColumns) {
            if (col.virtual) {
              let prop, cn;
              if (col.mm || (col.lk && col.lk.type === 'mm')) {
                const tn = col.mm ? col.mm.rtn : col.lk.ltn;
                const title = col.mm ? col.mm._rtn : col.lk._ltn;
                await this.$store.dispatch('meta/ActLoadMeta', {
                  env: this.nodes.env,
                  dbAlias: this.nodes.dbAlias,
                  tn,
                });

                prop = `${title}MMList`;
                cn = col.lk
                  ? col.lk._lcn
                  : (
                      this.$store.state.meta.metas[tn].columns.find(c => c.pv) ||
                      this.$store.state.meta.metas[tn].columns.find(c => c.pk) ||
                      {}
                    ).title;

                row[col.title] = r.row[prop] && r.row[prop].map(r => cn && r[cn]);
              } else if (col.hm || (col.lk && col.lk.type === 'hm')) {
                const tn = col.hm ? col.hm.table_name : col.lk.ltn;
                const title = col.hm ? col.hm.title : col.lk._ltn;

                await this.$store.dispatch('meta/ActLoadMeta', {
                  env: this.nodes.env,
                  dbAlias: this.nodes.dbAlias,
                  tn,
                });

                prop = `${title}List`;
                cn = col.lk
                  ? col.lk._lcn
                  : (
                      this.$store.state.meta.metas[tn].columns.find(c => c.pv) ||
                      this.$store.state.meta.metas[tn].columns.find(c => c.pk)
                    ).title;
                row[col.title] = r.row[prop] && r.row[prop].map(r => cn && r[cn]);
              } else if (col.bt || (col.lk && col.lk.type === 'bt')) {
                const tn = col.bt ? col.bt.rtn : col.lk.ltn;
                const title = col.bt ? col.bt._rtn : col.lk._ltn;
                await this.$store.dispatch('meta/ActLoadMeta', {
                  env: this.nodes.env,
                  dbAlias: this.nodes.dbAlias,
                  tn,
                });

                prop = `${title}Read`;
                cn = col.lk
                  ? col.lk._lcn
                  : (
                      this.$store.state.meta.metas[tn].columns.find(c => c.pv) ||
                      this.$store.state.meta.metas[tn].columns.find(c => c.pk) ||
                      {}
                    ).title;
                row[col.title] = r.row[prop] && r.row[prop] && cn && r.row[prop][cn];
              } else {
                row[col.title] = r.row[col.title];
              }
            } else if (col.uidt === 'Attachment') {
              let data = [];
              try {
                if (typeof r.row[col.title] === 'string') {
                  data = JSON.parse(r.row[col.title]);
                } else if (r.row[col.title]) {
                  data = r.row[col.title];
                }
              } catch {}
              row[col.title] = (data || []).map(a => `${a.title}(${a.url})`);
            } else {
              row[col.title] = r.row[col.title];
            }
          }
          return row;
        })
      );
    },
    async exportExcel() {
      let offset = 0;
      let c = 1;
      try {
        while (!isNaN(offset) && offset > -1) {
          const res = await this.getExportData({ offset, exportType: ExportTypes.EXCEL, responseType: 'base64' });
          const workbook = XLSX.read(res.data, { type: 'base64' });
          XLSX.writeFile(workbook, `${this.meta.title}_exported_${c++}.xlsx`);

          offset = +res.headers['nc-export-offset'];
          if (offset > -1) {
            this.$toast.info('Downloading more files').goAway(3000);
          } else {
            this.$toast.success('Successfully exported all table data').goAway(3000);
          }
        }
      } catch (e) {
        console.log(e);
        this.$toast.error(e.message).goAway(3000);
      }
    },
    async exportCsv() {
      let offset = 0;
      let c = 1;

      try {
        while (!isNaN(offset) && offset > -1) {
          const res = await this.getExportData({ offset, exportType: ExportTypes.CSV, responseType: 'blob' })
          const { data } = res;

          offset = +res.headers['nc-export-offset'];
          const blob = new Blob([data], { type: 'text/plain;charset=utf-8' });
          FileSaver.saveAs(blob, `${this.meta.title}_exported_${c++}.csv`);
          if (offset > -1) {
            this.$toast.info('Downloading more files').goAway(3000);
          } else {
            this.$toast.success('Successfully exported all table data').goAway(3000);
          }
        }
      } catch (e) {
        console.log(e);
        this.$toast.error(e.message).goAway(3000);
      }
    },
    async getExportData({ offset, exportType, responseType }) {
      let res;
      if (this.publicViewId) {
        res = await this.$api.public.csvExport(this.publicViewId, exportType, {
          responseType,
          query: {
            fields:
              this.queryParams &&
              this.queryParams.fieldsOrder &&
              this.queryParams.fieldsOrder.filter(c => this.queryParams.showFields[c]),
            offset,
            sortArrJson: JSON.stringify(
              this.reqPayload &&
                this.reqPayload.sorts &&
                this.reqPayload.sorts.map(({ fk_column_id, direction }) => ({
                  direction,
                  fk_column_id,
                }))
            ),
            filterArrJson: JSON.stringify(this.reqPayload && this.reqPayload.filters),
          },
          headers: {
            'xc-password': this.reqPayload && this.reqPayload.password,
          },
        });
      } else {
        res = await this.$api.dbViewRow.export(
          'noco',
          this.projectName,
          this.meta.title,
          this.selectedView.title,
          exportType,
          {
            responseType,
            query: {
              offset,
            },
          }
        );
      }
      return res;
    },
    async importData(columnMappings) {
      try {
        const data = this.parsedCsv.data;
        for (let i = 0, progress = 0; i < data.length; i += 500) {
          const batchData = data.slice(i, i + 500).map(row =>
            columnMappings.reduce((res, col) => {
              // todo: parse data
              if (col.enabled && col.destCn) {
                const v = this.meta && this.meta.columns.find(c => c.title === col.destCn);
                let input = row[col.sourceCn];
                // parse potential boolean values
                if (v.uidt == UITypes.Checkbox) {
                  input = input.replace(/["']/g, '').toLowerCase().trim();
                  if (input == 'false' || input == 'no' || input == 'n') {
                    input = '0';
                  } else if (input == 'true' || input == 'yes' || input == 'y') {
                    input = '1';
                  }
                } else if (v.uidt === UITypes.Number) {
                  if (input == '') {
                    input = null;
                  }
                } else if (v.uidt === UITypes.SingleSelect || v.uidt === UITypes.MultiSelect) {
                  if (input == '') {
                    input = null;
                  }
                }
                res[col.destCn] = input;
              }
              return res;
            }, {})
          );
          await this.$api.dbTableRow.bulkCreate('noco', this.projectName, this.meta.title, batchData);
          progress += batchData.length;
          this.$store.commit('loader/MutMessage', `Importing data : ${progress}/${data.length}`);
          this.$store.commit('loader/MutProgress', Math.round((100 * progress) / data.length));
        }
        this.columnMappingModal = false;
        this.$store.commit('loader/MutClear');
        this.$emit('reload');
        this.$toast.success('Successfully imported table data').goAway(3000);
      } catch (e) {
        this.$toast.error(e.message).goAway(3000);
      }
    },
  },
};
</script>

<style scoped></style>
