<template>
  <div class="q-pa-md q-col-gutter-y-md redesigned Import">
    <div class="row q-gutter-sm">
      <div class="col-12 col-sm section-header">Импорт данных о гражданах</div>
    </div>
    <q-card>
      <q-card-section>
        <r-file-loader
          ref="xmlFileLoader"
          :multiply="true"
          :activity-info="citizenActivityInfo"
          supportedFile=".xml"
          @change="changeFileXmlArr"
        />
      </q-card-section>
      <q-card-actions>
        <r-btn
          :disable="!filesXmlArr.length"
          :title="!filesXmlArr.length && 'Нет файла для загрузки'"
          label="Загрузить"
          @click="loadFileXmlArr"
        />
      </q-card-actions>
    </q-card>
    <q-dialog
      v-model="requestImportDialog"
      class="ImportDialogRequest"
      no-backdrop-dismiss
      @hide="clearSelectedXmlFiles()"
    >
      <q-card
        class="ImportDialogRequest-Card"
        :class="{ 'ImportDialogRequest-Card__Fat': isImportDialogArrMode }"
      >
        <q-btn
          :disable="isLoading"
          flat
          round
          class="ImportDialogRequest-Card__Close"
          icon="pgs-close"
          size="sm"
          text-color="#1491CF"
          v-close-popup
        />
        <q-card-section class="ImportDialog-AttentionContent">
          <q-icon
            class="ImportDialog-AttentionContent__Icon"
            name="pgs-import"
          />
          <p class="ImportDialog-AttentionContent__Header">
            {{ headerImportDialog }}
          </p>
          <re-table
            v-if="isImportDialogArrMode"
            :data="tableData"
            :columns="columns"
            :rows-per-page-options="[10, 25, 50, 100]"
            grid-header
            wrap-cell
          >
            <template #body-item-progress="props">
              <q-linear-progress rounded :value="props.row.progress" />
            </template>
            <template #body-item-statusLoad="props">
              <div
                :class="{
                  '--red': isNotProcessed(props.row),
                }"
                class="StatusBox"
              >
                <span class="StatusBox-Text">
                  {{ statusText(props.row) }}
                </span>
              </div>
            </template>
          </re-table>
          <div v-else class="ImportDialog-AttentionContent__Noty">
            Данные загружены и ожидают конвертации. История операций доступна в
            журнале загрузок
          </div>
        </q-card-section>
        <q-card-actions class="ImportDialog-AttentionButtons">
          <r-btn
            v-close-popup
            :disable="isLoading"
            class="ImportDialog-AttentionButton"
            label="Закрыть"
          />
          <r-btn
            v-show="!isLoading"
            :action="true"
            :label="labelActionBtn"
            class="ImportDialog-AttentionButton"
            @click="goToLogImport()"
          />
        </q-card-actions>
      </q-card>
    </q-dialog>
    <div class="row q-gutter-sm">
      <div class="col-12 col-sm section-header">
        Загрузка сведений воинского учёта
      </div>
    </div>
    <q-card>
      <q-card-section>
        <r-file-loader
          ref="csvFileLoader"
          :multiply="false"
          :activity-info="militaryActivityInfo"
          supportedFile=".csv"
          @change="addFileCsv"
        />
      </q-card-section>
      <q-card-actions>
        <r-btn
          :disable="!filesCsv"
          :title="!filesCsv && 'Нет файла для загрузки'"
          label="Загрузить"
          @click="loadFileCsv"
        />
      </q-card-actions>
    </q-card>

    <q-dialog v-model="apiErrorShow" no-backdrop-dismiss>
      <process-api-error
        :errorObject="errorObject"
        :route-btn="processApiErrorRoutBtn"
      />
    </q-dialog>

    <screen-blocker v-if="isShowBlocker">
      <progress-bar :progress="uploadPercentage">
        Идет процесс загрузки
      </progress-bar>
    </screen-blocker>
  </div>
</template>

<style lang="scss" scoped>
@import '@/recruits/assets/styles/extends.scss';

::v-deep {
  .DownloadTable {
    &-Header {
      @extend %table-header;
    }
  }
}

.section-header {
  font-family: 'Golos Text', sans-serif;
  font-style: normal;
  font-weight: 500;
  font-size: 28px;
  line-height: 32px;
  letter-spacing: -0.02em;
  color: rgba(2, 12, 18, 0.9);
  margin-bottom: 20px;
}
.ButtonsBlock {
  align-items: center;
}
.DopButton {
  box-sizing: border-box;
  background-color: rgba(15, 32, 160, 0.08);
  border-radius: 20px;
  padding: 4px;
  margin-top: -4px;
  margin-bottom: -4px;
  &-TextFiled {
    margin-left: 8px;
    margin-right: 8px;
    font-family: 'Golos UI', sans-serif;
    font-style: normal;
    font-weight: 400;
    font-size: 14px;
    line-height: 16px;
    color: rgba(2, 12, 18, 0.58);
    &__Bold {
      font-weight: 500;
      color: #015e98;
    }
  }
  &-Button {
    &__Color {
      &__DarkBlue {
        background: linear-gradient(92.91deg, #1491cf 0%, #0f68a0 100%);
        box-shadow: 0 0.6px 1.8px rgba(7, 46, 71, 0.12),
          0 3.2px 8px rgba(7, 46, 71, 0.15);
        color: rgba(255, 255, 255, 0.92);
      }
    }
  }
}
.ImportDialogRequest {
  &-Card {
    display: flex;
    flex-direction: column;
    &__Close {
      flex: 0 0 auto;
      color: #1491cf;
      align-self: flex-end;
    }
    &__Fat {
      max-width: 1200px;
      width: 1200px;
    }
  }
  &-Header {
    color: rgba(2, 12, 18, 0.9);
    font-size: 20px;
    font-family: 'Golos Text', sans-serif;
    font-weight: 500;
    line-height: 24px;
    letter-spacing: -0.3px;
  }
  &-Content {
    &__Info {
      margin-bottom: 20px;
    }
    &__Message {
      font-size: 16px;
      font-family: 'Golos Text', sans-serif;
    }
  }
  &-Total,
  &-New,
  &-Update {
    color: rgba(2, 12, 18, 0.8);
    font-size: 18px;
    font-family: 'Golos Text', sans-serif;
    font-weight: 500;
    line-height: 24px;
    letter-spacing: -0.3px;
    &:not(:last-child) {
      margin-right: 15px;
    }
  }
  &-Dialog {
    min-width: 1000px;
    background-color: rgb(245 248 251);
  }
  &-TableHeader {
    padding: 6px 0;
    color: rgba(2, 12, 18, 0.58);
    overflow: hidden;
    font-family: 'Golos Text', sans-serif;
    font-style: normal;
    font-weight: 600;
    font-size: 10px;
    line-height: 12px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
  }
  &-TableRow {
    display: grid;
    grid-template-columns: 2fr 2fr 2fr 3fr;
    border-bottom: 1px solid rgba(1, 72, 116, 0.11);
    padding: 4px 0;
  }
  &-TableCell {
    color: rgba(2, 12, 18, 0.58);
    font-size: 12px;
    font-family: 'Golos UI', sans-serif;
    line-height: 14px;
    font-weight: 500;
  }
  &-ErrorString {
    &:not(:first-child) {
      padding: 2px;
    }
  }
  &-Actions {
    background-color: rgb(245 245 245);
    display: flex;
    justify-content: flex-end;
  }
}
.ImportDialog {
  &-AttentionContent {
    * {
      margin: 0;
      padding: 0;
    }
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 30px 40px;
    &__Icon {
      color: #1491cf;
      font-size: 40px;
      margin-bottom: 10px;
    }
    &__Header {
      font-family: 'Golos Text', sans-serif;
      font-style: normal;
      font-weight: 500;
      font-size: 20px;
      line-height: 24px;
      text-align: center;
      letter-spacing: -0.015em;
      color: #015e98;
      margin-bottom: 24px;
    }
    &__Noty {
      background: rgba(227, 254, 250, 1);
      border-left: 2px solid rgba(4, 150, 114, 1);
      padding: 8px;

      font-family: 'Golos UI', sans-serif;
      font-style: normal;
      font-weight: 400;
      font-size: 14px;
      line-height: 16px;
      text-align: center;
      color: rgba(4, 150, 125, 1);
    }
  }
  &-AttentionButtons {
    display: flex;
    justify-content: space-between;
    padding: 12px 20px;
  }
  &-AttentionButton {
    padding: 0 10px;
  }
}
.flip-icon::v-deep {
  i {
    transform: scaleX(-1);
  }
}
.StatusBox {
  display: inline-flex;
  align-items: center;
  text-align: center;
  padding: 0 4px 0 4px;
  font-family: 'Golos Text', sans-serif;
  width: 100%;
  height: 100%;
  background: rgba(53, 126, 172, 0.05);
  color: rgba(2, 12, 18, 0.58);

  &-Text {
    width: 100%;
    font-weight: 500;
    letter-spacing: 0.05em;
    font-size: 10px;
    font-style: normal;
    line-height: 12px;
    text-transform: uppercase;

    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2; /* number of lines to show */
    line-clamp: 2;
    -webkit-box-orient: vertical;
  }

  &_Loading {
    padding: 0;
  }

  &.registered,
  &.processed {
    background: #e3fefa;
    color: #04967d;
    border-color: #04967d;
  }

  &.received {
    background: #ddf0f9;
  }

  &.failed_to_attend,
  &.rejected {
    background: #fdf3ed;
    color: #cf5f14;
    border-color: #cf5f14;
  }

  &.restrictions_imposed,
  &.--red {
    background: #fee3e7;
    color: #c10525;
    border-color: #c10525;
  }

  &.no-border {
    border-width: 0;
  }

  .skeleton {
    border-radius: 2px;
  }
}
</style>

<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator';
import {
  ImportResponse,
  PersonRegistryApi,
} from '@/recruits/openapi/person-registry';
import { ImportXmlResponseV1, ImportApi } from '@/recruits/openapi/import';
import {
  callMethodOnComponent,
  formatBytes,
  formatDate,
} from '@/recruits/utils';
import ProcessApiError from '@/recruits/components/dialogs/ProcessApiError.vue';
import ScreenBlocker from '@components/common/screen-blocker/index.vue';
import ProgressBar from '@components/common/progress-bar/index.vue';
import { AxiosRequestConfig } from 'axios';
import { RTableColumn } from '@/interfaces';
import ReTable from '@/recruits/components/common/r-table/index.vue';
import StatusBox from '@/recruits/components/common/status-box/index.vue';
import { ActivityInfo as FileLoaderActivityInfo } from '@components/common/r-file-loader/interfaces';

interface RouteBtn {
  label: string;
  routeName: string;
}
interface FileInfo {
  file: File;
  fileName: string;
  fileSize: string;
}
interface LoadData extends ImportXmlResponseV1 {
  progress: number;
  isPending: boolean;
}

const LOG_IMPORT_ROUT_NAME = 'log-import';
const LOG_IMPORT_ROUT_LABEL = 'Журнал загрузок';
const FULL_LOAD_VALUE = 1;

@Component({
  components: {
    ProcessApiError,
    ScreenBlocker,
    ProgressBar,
    ReTable,
    StatusBox,
  },
  methods: {
    formatDate,
    formatBytes,
  },
})
export default class Archive extends Vue {
  XmlApi = new PersonRegistryApi();
  ImportApi = new ImportApi();
  public loading = false;
  filesXmlArr: FileInfo[] | [] = [];
  filesCsv: File | null = null;
  requestImportDialog = false;
  isImportDialogArrMode = false;
  requestCsvImportData: ImportResponse;
  tableData: LoadData[] = [];

  apiErrorShow = false;
  errorObject = {};
  uploadPercentage = 0;
  isShowBlocker = false;
  isLoading = false;

  @Watch('uploadPercentage')
  checkUpload(uploadPercentage: number): void {
    if (uploadPercentage === 100) {
      this.uploadPercentage = 0;
      this.isShowBlocker = false;

      this.$q.loading.show({
        message: 'Обработка данных',
      });
    }
  }

  get citizenActivityInfo(): FileLoaderActivityInfo {
    return {
      select: {
        buttonCode: 'import_citizen_select',
      },
      delete: {
        buttonCode: 'import_citizen_delete',
      },
    };
  }

  get militaryActivityInfo(): FileLoaderActivityInfo {
    return {
      select: {
        buttonCode: 'import_military_select',
      },
      delete: {
        buttonCode: 'import_military_delete',
      },
    };
  }

  isFileLoaded(progress: LoadData['progress']): boolean {
    return progress === FULL_LOAD_VALUE;
  }

  statusText(row: LoadData): string {
    return this.isFileLoaded(row.progress)
      ? row.isPending
        ? 'Идёт обработка'
        : row.statusLoad
        ? 'Обработано'
        : 'Не обработано'
      : row.isPending
      ? 'Идёт загрузка'
      : 'Ожидание';
  }

  isNotProcessed(row: LoadData): boolean {
    return this.isFileLoaded(row.progress) && !row.isPending && !row.statusLoad;
  }

  clearSelectedXmlFiles(): void {
    callMethodOnComponent(this.$refs.xmlFileLoader, 'clearResults');
  }

  importOptions = (that = this): AxiosRequestConfig => {
    return {
      onUploadProgress: (progressEvent: {
        loaded: number;
        total: number;
      }): void => {
        const { loaded, total } = progressEvent;
        that.uploadPercentage = Math.round((loaded / total) * 100);
      },
    };
  };

  changeFileXmlArr(info: FileInfo[]): void {
    this.filesXmlArr = info;
    this.tableData = this.filesXmlArr.map((fileInfo) => {
      return {
        id: '',
        fileName: fileInfo.fileName,
        descName: '',
        statusLoad: false,
        progress: 0,
        isPending: false,
      };
    });
  }

  addFileCsv(file: FileInfo[] | undefined): void {
    this.filesCsv = file?.length ? file[0].file : null;
  }

  async loadFileXmlArr(): Promise<void> {
    this.$recordActivity({
      buttonCode: 'import_citizen_upload',
      extraInfo: { file: this.filesXmlArr, type: 'file' },
    });

    this.isImportDialogArrMode = true;
    this.isLoading = true;
    this.requestImportDialog = true;

    for (let index = 0; index < this.tableData.length; index++) {
      this.$set(this.tableData, index, {
        ...this.tableData[index],
        isPending: true,
      });
      const { data } = await this.ImportApi.importFileXmlV1(
        [this.filesXmlArr[index].file],
        {
          onUploadProgress: (progressEvent: {
            loaded: number;
            total: number;
          }): void => {
            const { loaded, total } = progressEvent;
            this.$set(this.tableData, index, {
              ...this.tableData[index],
              progress: Math.round((loaded / total) * 10) / 10,
            });
          },
        }
      );
      this.$set(this.tableData, index, {
        ...this.tableData[index],
        ...data[0],
        isPending: false,
      });
    }
    this.isLoading = false;
  }

  async loadFileCsv(): Promise<void> {
    if (!this.filesCsv) return;
    this.isShowBlocker = true;

    try {
      const { data } = await this.XmlApi.importFileCsv(
        this.filesCsv,
        this.importOptions(this)
      );
      this.requestCsvImportData = data;
      this.isImportDialogArrMode = false;
      this.requestImportDialog = true;
    } catch (error) {
      this.isShowBlocker = false;

      if (error.message !== 'Network Error') {
        if (error.response.status !== 400) {
          this.errorObject = {
            data: {
              message: 'Сервис временно недоступен, попробуйте повторить позже',
              description:
                'Возникла ошибка сетевого соединения. Удаленный сервер недоступен. ' +
                ' Проверьте сетевое соединение или обратитесь к системному администратору',
            },
          };
        } else {
          this.errorObject = error.response;
        }
      } else {
        this.errorObject = {
          data: {
            message: 'Сервис временно недоступен, попробуйте повторить позже',
            description:
              'Возникла ошибка сетевого соединения. Удаленный сервер недоступен. ' +
              ' Проверьте сетевое соединение или обратитесь к системному администратору',
          },
        };
      }
      this.apiErrorShow = true;
    } finally {
      this.$recordActivity({
        buttonCode: 'import_military_upload',
        extraInfo: { file: this.filesCsv, type: 'file' },
      });
      this.$q.loading.hide();
      callMethodOnComponent(this.$refs.csvFileLoader, 'clearResults');
    }
  }

  goToLogImport(): void {
    this.$router.push({ name: LOG_IMPORT_ROUT_NAME });
  }

  get headerImportDialog(): string {
    return this.isImportDialogArrMode
      ? this.isLoading
        ? 'Дождитесь завершения загрузки файлов'
        : 'Результаты загрузки файлов'
      : 'Загрузка файла завершена';
  }

  get labelActionBtn(): string {
    return this.isImportDialogArrMode
      ? LOG_IMPORT_ROUT_LABEL
      : 'Перейти в журнал';
  }

  get columns(): RTableColumn<Array<ImportXmlResponseV1>>[] {
    return [
      {
        name: 'fileName',
        label: 'Имя файла',
        field: 'fileName',
        sortable: true,
        align: 'left',
        width: '30%',
        headerStyle: 'width: 30%',
        headerClasses: 'DownloadTable-Header',
      },
      {
        name: 'descName',
        label: 'Описание',
        field: 'descName',
        width: '25%',
        headerStyle: 'width: 25%',
        align: 'center',
        headerClasses: 'DownloadTable-Header',
      },
      {
        name: 'description',
        label: 'Подробности',
        field: 'description',
        format: (val) => (val ? val.toString() : '—'),
        width: '25%',
        headerStyle: 'width: 25%',
        align: 'center',
        headerClasses: 'DownloadTable-Header',
      },
      {
        name: 'progress',
        label: 'Прогресс',
        field: 'progress',
        width: '10%',
        headerStyle: 'width: 10%',
        align: 'center',
        borderless: true,
        headerClasses: 'DownloadTable-Header',
      },
      {
        name: 'statusLoad',
        label: 'Статус',
        field: 'statusLoad',
        width: '10%',
        headerStyle: 'width: 10%',
        align: 'center',
        collaps: true,
        headerClasses: 'DownloadTable-Header',
      },
    ];
  }

  get processApiErrorRoutBtn(): RouteBtn {
    return {
      label: LOG_IMPORT_ROUT_LABEL,
      routeName: LOG_IMPORT_ROUT_NAME,
    };
  }
}
</script>
