import { Component, Mixins, Prop, Vue, Watch } from 'vue-property-decorator';
import { oidcClientComputed } from '@state/modules/oidc-client';
import StatementsSearchBar from './components/search-bar.vue';
import { pushRoute } from '@utils/routes';
import { convertDateFormatWithoutLocale, convertDateFormat} from '@utils/format-date';
import { DateFormat } from '@constants/utils/format-date';
import { EnvOchnVisibility } from '@constants/types/env';
import { saveFileViaLink } from '@utils/files';
import { DictionaryApi, ServiceApi } from '@openapi/registry-service';
import { notifyError, notifyInfo, notifySuccess } from '@utils/notify';
import { TableColumn } from '@/interfaces';
import { regions } from '@constants/entities/Region/Regions';
import { WithScopeAccess } from '@interfaces/entities/Access';
import { USER_SCOPES } from '@constants/entities/Scope';
import { getReversedScopes } from '@utils/entities/UserScope';
import SearchQuery from '@interfaces/entities/SearchQuery';
import { DocumentApi, CSVDocumentsApi } from '@openapi/document-registry';
import { DocumentModel } from '@interfaces/base/DocumentModel';
import { getServicesOfOrganization } from '@/services/registry-service';
import { formatResponse, addressInfo } from './utils/formatResponse'
import { ExportApi, ReportResponse } from '@openapi/refugee-compensation';
import S3UrlSignerFilesService from '@services/s3urlsigner/files';
import { saveAs } from 'file-saver';
import last from 'lodash/last';
import cloneDeep from 'lodash/cloneDeep';
import { defaultColumnsExtraFields, fillBooleanValues, getColumnsFromLocalStorage, removeColumnsFromLocalStorage, removeServiceIdFromQuery, saveColumnsToLocalStorage } from './utils/columns';
import { DocumentConverterApi, LoadFromDRAndConvertRequestModelExtensionEnum } from '@openapi/document-converter';
import set from 'lodash/set';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';

export interface Columns {
  visible: WithScopeAccess<TableColumn>[];
  hidden: WithScopeAccess<TableColumn>[];
}

const dictionaryApi = new DictionaryApi();
const serviceApi = new ServiceApi(); 
const documentCoverterApi = new DocumentConverterApi();
const documentApi = new DocumentApi();
const cSVDocumentsApi = new CSVDocumentsApi();
const getFilenameByUrl = (url: string) => last(url.split('/'));

const ACCESS_SERVICE_ELECTRONIC_DUBLICATE = '93493fd9-81ea-419e-b14c-e2142e9d8a17';
const ACCESS_SERVICE_REFUGEE_COMPENSATION = 'b68630f4-18c4-4f2f-9a1e-da70144d6390';

const statements = Vue.extend({
  computed: {
    ...oidcClientComputed,
  },
});

@Component({
  components: { StatementsSearchBar },
})
export default class Statements extends Mixins(statements) {
  @Prop() searchQuery: StatementsSearchQuery | undefined;

  USER_SCOPES = USER_SCOPES;

  private sortByDefault = 'modified';

  model: Omit<DocumentModel, 'loading'> & { loading: number } = {
    loading: 0,
    onlyMy: (String(this.$route.query.onlyMy).toLowerCase() === 'true') ? true : false,
    pagination: {
      descending:
        this.$route.query.descending === undefined
          ? true
          : this.$route.query.descending === 'true',
      rowsPerPage: this.$route.query.rowsPerPage
        ? Number(this.$route.query.rowsPerPage)
        : 10,
      sortBy: this.$route.query.sortBy
        ? this.$route.query.sortBy
        : this.sortByDefault,
      rowsNumber: 0,
      page: this.$route.query.page ? Number(this.$route.query.page) : 1,
    },
    records: []
  };

  columns: Columns = {
    visible: [],
    hidden: []
  };

  defaultCols: WithScopeAccess<TableColumn>[] = [];

  selectedServiceFound = false;
  selectedServiceMapping: any = null;
  dictionaries: any[] = [];
  expand = 'applicant,Region';
  prevSearchQuery: any = null;

  get cols() {
    const columns = this.selectedServiceId ? this.columns.visible : this.defaultCols;
    const scopedColumns = this.$AC.scopesSift<TableColumn>(columns);
    return scopedColumns;
  }

  @Watch('statementsRowsNumber', {
    immediate: true,
  })
  private statementsRowsNumberHandler(val: number): void {
    this.model.pagination.rowsNumber = val;
  }

  get selectedServiceId(): string {
    return this.searchQuery?.services!;
  }

  get isFetchedAfterFilterChanged() {
    return this.prevSearchQuery === null || isEqual(this.prevSearchQuery, this.searchQuery);
  }

  @Watch('searchQuery')
  onSearchFilterChange(searchQuery: any) {
    if (searchQuery.services) {
      this.fetchService().then(async () => {
        this.initColumns();
        this.selectedServiceFound = true;
        await this.fetchDictionaries();
      }).catch((e) => {
        if (e instanceof Error) {
          notifyError(e.message);
        }
        this.selectedServiceFound = false;
      });
    } else {
      this.selectedServiceFound = false;
      this.selectedServiceMapping = null;
      this.dictionaries = [];
    }
  }

  private get showPersonalReception(): boolean {
    if (
      String(process.env.VUE_APP_OCHN_PRIEM_VISIBILITY) in EnvOchnVisibility
    ) {
      switch (process.env.VUE_APP_OCHN_PRIEM_VISIBILITY) {
        case EnvOchnVisibility.VISIBLE_FOR_ALL:
          return true;
        case EnvOchnVisibility.VISIBLE_FOR_ROLE_BEARERS:
          return this.$AC.haveScope(USER_SCOPES.PERSONAL_RECEPTION);
      }
    }
    return false;
  }

  private defaultColumns(): WithScopeAccess<TableColumn>[]{
    return [
      {
        data: {
          align: 'left',
          field: (val) => val,
          formatField: 'common.orderId',
          handler: (row, options) => {
            pushRoute(
              this.$router,
              {
                name: 'cards',
                params: {
                  id: row as string,
                  path: 'statements',
                },
                query: {
                  active: 'author',
                  components: this.$store.getters[
                    'cardComponents/statements/components'
                  ],
                },
              },
              options?.targetBlank
            );
          },
          handlerField: 'id',
          label: this.$t('statements.regNumber'),
          name: 'common.orderId',
          required: true,
          sortable: true,
        },
      },
      {
        data: {
          align: 'left',
          field: 'system.shortName',
          label: this.$t('statements.serviceType'),
          name: 'system.service',
          required: true,
          sortable: true,
          isHTML: true,
          style: 'white-space: normal;',
        },
      },
      {
        data: {
          align: 'left',
          field: 'system.applicant.fullName',
          icon: 'people_outline',
          label: this.$t('statements.applicant'),
          name: 'system.applicant.fullName',
          required: true,
          sortable: true,
          isHTML: true,
          style: 'white-space: normal;',
        },
      },
      {
        data: {
          align: 'left',
          field: 'system.status.internal',
          format: (val) =>
            this.$store.getters[
              'registryService/referenceBooks/appliedReferenceBook'
            ]({
              key: val,
              id: 'gpzu_2_application_status',
              valueName: 'description',
            }).value,
          label: this.$t('statements.status'),
          name: 'system.status.internal',
          required: true,
          sortable: true,
          isHTML: true,
          style: 'white-space: normal;',
        },
      },
      {
        data: {
          align: 'left',
          field: 'common.assignee.fullName',
          icon: 'people_outline',
          label: this.$t('statements.assignee'),
          name: 'common.assignee.fullName',
          style: 'white-space: pre-wrap',
          required: true,
          sortable: true,
        },
      },
      {
        access: {
          openedTo: getReversedScopes([USER_SCOPES.RF_OBSERVER_SCOPE]),
        },
        data: {
          align: 'left',
          field: 'common.chanel',
          label: this.$t('statements.receiveChannel'),
          name: 'common.chanel',
          required: true,
          sortable: true,
        },
      },
      {
        data: {
          align: 'left',
          field: 'system.startDate',
          format: (val: any) => {
            if (val) {
              return val;
            }
            return '';
          },
          label: this.$t('statements.dateCreate'),
          name: 'system.startDateIso',
          required: true,
          sortable: true,
        },
      },
      {
        data: {
          align: 'left',
          field: 'common.dueDate',
          format: (val: any) =>
            val ? convertDateFormatWithoutLocale(val) : '',
          label: this.$t('statements.maxDate'),
          name: 'common.dueDate',
          required: true,
          sortable: true,
        },
      },
      {
        data: {
          align: 'left',
          field: 'common.finishDate',
          format: (val: any) =>
            val ? convertDateFormatWithoutLocale(val) : '',
          label: this.$t('statements.finishDate'),
          name: 'common.finishDate',
          required: true,
          sortable: true,
        },
      },
      {
        access: {
          openedTo: getReversedScopes([USER_SCOPES.RF_OBSERVER_SCOPE]),
        },
        data: {
          align: 'left',
          field: (val: any) => val,
          format: (val: any) => {
            return addressInfo(val);
          },
          label: this.$t('statements.applicantAddress'),
          name: 'reg-address',
          required: true,
          sortable: false,
          isHTML: true,
          style: 'white-space: normal;',
        },
      },
      {
        access: {
          openedTo: [USER_SCOPES.RF_OBSERVER_SCOPE],
        },
        data: {
          align: 'left',
          field: 'system.regionCode',
          icon: 'people_outline',
          label: this.$tc('entities.region.name', 0),
          name: 'system.regionCode',
          style: 'white-space: pre-wrap',
          format: (code) => {
            return (
              regions.find((region) => region.code === code)?.title ??
              'Неизвестный субъект'
            );
          },
          required: true,
          sortable: true,
        },
      },
      {
        access: {
          openedTo: [USER_SCOPES.OBSERVER],
        },
        data: {
          align: 'left',
          field: 'system.department.fullName',
          icon: 'people_outline',
          label: this.$t('statements.organization'),
          name: 'system.department.fullName',
          style: 'white-space: pre-wrap',
          required: true,
          sortable: true,
        },
      },
    ];
  }

  resetModel(): void {
    this.model.records = [];
  }

  // todo check - planning to change to backend generation https://jira.devsun.ru/browse/PGS-6207
  private async saveData(): Promise<void> {
    const query = [];
    if (this.queryString) {
      query.push(`(${this.queryString})`);
    }

    const assigneeId = this.currentUser.sub;
    if (this.model.onlyMy && assigneeId) {
      query.push(`common.assignee.id==${assigneeId}`);
    }

    this.model.loading++;
    cSVDocumentsApi
      .downloadCSVFileRegistries(query.join(' and '), this.fulltextString)
      .then(async (response: any) => {
        const formattedResponce = response.data || [];
        this.saveCsv(formattedResponce);
      })
      .catch((e) => {
        notifyError(e.message.toString());
      })
      .finally(() => {
        this.model.loading--;
      });
  }

  private async saveRefugeeFile(): Promise<void> {
    if (!this.searchQuery) {
      return notifyError(this.$t('statements.Common.notification.queryIsEmpty').toString())
    }
    const {
      status,
      daterange,
      daterangeDecision,
      services,
    } = this.searchQuery;

    const [dateFrom, dateTo] = (daterange?.split(',') || []).map((date) =>
      convertDateFormat(date, DateFormat.date, DateFormat.invertedDate)
    );

    const [dateFromDecision, dateToDecision] = (daterange?.split(',') || []).map((date) =>
      convertDateFormat(date, DateFormat.date, DateFormat.invertedDate)
    );

    const organization = await this.$store.dispatch(
      'idm/organizations/getOrganization',
      { id: this.currentUser.domain_id },
    );

    this.model.loading++;
    try {
      const exportApi = new ExportApi();
      const { data } = await exportApi
        .getReportData(organization.id, status, dateFrom, dateTo, services);
      if (data.s3ExcelLink) {
        const { file } = await S3UrlSignerFilesService.fetchFileFromS3ByUrl({
          url: data.s3ExcelLink
        });
        saveAs(file, getFilenameByUrl(data.s3ExcelLink));
        notifySuccess(this.$t('statements.Common.notification.success').toString());
      }
      if (!data.processedRowsCount) {
        notifyInfo(this.$t('statements.Common.notification.noData').toString())
      }
    } catch (error) {
      const e = error as Error
      notifyError(e.message.toString());
    } finally {
      this.model.loading--;
    }
  }

  private get sort(): string {
    return `${this.model.pagination.descending ? '-' : '+'}${
      this.model.pagination.sortBy || this.sortByDefault
    }`;
  }

  private saveCsv(content: string): void {
    if (content) {
      saveFileViaLink({
        anchor: this.$refs.csvLink as HTMLAnchorElement,
        hrefData: 'data:text/csv;charset=utf-8,%EF%BB%BF' + encodeURI(content),
        fileName: 'registry_data.csv',
      });
    }
  }

  private goTo(path: string) {
    this.$router.push({
      name: path,
    });
  }

  private getDictionary(val: string) {
    return this.$store.getters[
      'registryService/referenceBooks/appliedReferenceBook'
    ]({
      key: val,
      id: 'gpzu_2_application_status',
      valueName: 'description',
    }).value
  }

  async created() {
    this.defaultCols = this.defaultColumns();

    await this.$store.dispatch(
      'registryService/referenceBooks/initReferenceBooks',
      [
        {
          force: true,
          id: 'gpzu_2_application_status',
        },
      ]
    );
  }

  @Watch('model.onlyMy')
  protected updateOnlyMy() {
    this.$store.commit(
      'documentRegistry/statements/statementsOnlyMy',
      this.model.onlyMy
    );
    this.fetch();
  }

  private queryString = '';
  private fulltextString = '';
  private fullQuery = '';
  private pending = 0;

  private async fetch(q?: SearchQuery) {
    const prevQuery = this.fullQuery;
    const prevFulltext = this.fulltextString;
    const query = [];
    if (typeof q !== 'undefined') {
      this.queryString = q?.query || '';
      this.fulltextString = q?.fulltext || '';
    }

    if (this.queryString) {
      query.push(`${this.queryString}`);
    }

    // @ts-ignore
    const assigneeId = this.currentUser.sub;
    if (this.model.onlyMy && assigneeId) {
      query.push(`common.assignee.id==${assigneeId}`);
    }

    if (this.pending) {
      this.pending++;
      return;
    }
    this.pending = 1;
    this.fullQuery = query.join(' and ');

    try {
      this.model.loading++;
      const { data: statementsPaging } = await documentApi.pagingDocuments(
        'Applications',
        this.model.pagination.page,
        this.model.pagination.rowsPerPage,
        this.fullQuery,
        this.fulltextString,
        this.sort,
        this.expand
      );
      this.model.pagination.rowsNumber = statementsPaging?.totalRows || 0;
      statementsPaging.records = this.fillDictionaryValues(statementsPaging?.records);
      this.model.records = fillBooleanValues(statementsPaging?.records, this.selectedServiceMapping);
      this.prevSearchQuery = cloneDeep(this.searchQuery);
    } catch (e) {
      notifyError(e.message.toString());
    } finally {
      this.$store
        .dispatch('registryService/referenceBooks/initReferenceBooks', [
          {
            force: true,
            id: 'gpzu_2_application_status',
          },
        ])
        .finally(() => {
          this.model.loading--;
        });
    }

    if (this.pending > 1) {
      this.pending = 0;
      await this.fetch();
      return;
    }
    this.pending = 0;
  }

  private init(val?: any): void {
    if (val?.pagination) {
      this.model.pagination.descending = val.pagination.descending;
      this.model.pagination.rowsPerPage = val.pagination.rowsPerPage;
      this.model.pagination.page = val.pagination.page;
      this.model.pagination.sortBy =
        val.pagination.sortBy == null
          ? this.sortByDefault
          : val.pagination.sortBy;
    }
    this.fetch();
  }

  resetPagination(pagination: any): void {
    if (pagination) {
      Object.assign(this.model.pagination, { ...pagination });
    } else {
      this.model.pagination.page = 1;
    }
  }

  private async ownershipSwitched(val: string): Promise<void> {
    if (val === 'mine') {
      this.model.onlyMy = true;
    } else { // 'all'
      this.model.onlyMy = false;
    }
  }

  successfulAccessElectronicDublicate = false;
  successfulAccessRefugeeCompensation = false;

  async mounted(): Promise<void> {
    try {
      const serviceIds = await getServicesOfOrganization(
        this.currentUser.domain_id
      );

      this.successfulAccessElectronicDublicate = !!serviceIds.find(
        (id) => id === ACCESS_SERVICE_ELECTRONIC_DUBLICATE
      );

      this.successfulAccessRefugeeCompensation = !!serviceIds.find(
        (id) => id === ACCESS_SERVICE_REFUGEE_COMPENSATION
      );
    } catch (error) {
      console.log(error);
      if (error instanceof Error) notifyError(error.message);
    }
  }

  private initColumns() {
    this.columns = getColumnsFromLocalStorage(
      this.selectedServiceId, this.defaultCols, this.selectedServiceMapping
    );
  }

  private clearStatementsStorage() {
    removeColumnsFromLocalStorage(this.selectedServiceId);
  }

  private async downloadExportMapping() {
    this.model.loading++;
    try {
      let columns = this.cols.map((c: any) => ({
        name: c.formatField ||
          (c.name === 'reg-address'
            ? 'applicant.addressInfo.adressRegistration.fullAddress'
            : c.field),
        label: c.label
      }));
      columns = columns.map((c: any) => ({
        ...c,
        ...defaultColumnsExtraFields[c.name] && defaultColumnsExtraFields[c.name],
      }));
      const query = encodeURI(removeServiceIdFromQuery(this.fullQuery, this.selectedServiceId));
      const extension = LoadFromDRAndConvertRequestModelExtensionEnum.Xlsx;
      const frontend = process.env.VUE_APP_PROJECT_PATH?.replaceAll('/', '');
      const { data } = await documentCoverterApi.loadAndConvertDocumentRegistryDocuments({
        columns,
        extension,
        query,
        collectionName: 'Applications',
        expand: this.expand,
        serviceId: this.selectedServiceId,
        frontend,
      });
      if (data.isLoaded && data.loadId) {
        const { data: blob } = await documentCoverterApi.getUploadedFile(data.loadId, { responseType: 'blob' });
        saveAs(blob, `Экспорт_по_услуге.${extension}`);
      } else {
        notifyInfo('Ссылка на скачивание выгрузки будет доступна на вкладке уведомлений');
      }
    } catch (e) {
      if (e instanceof Error) {
        notifyError(e.message);
      }
    } finally {
      this.model.loading--;
    }
  }

  private changeColumns(visible: WithScopeAccess<TableColumn>[], hidden: WithScopeAccess<TableColumn>[]) {
    this.columns.visible = cloneDeep(visible);
    this.columns.hidden = cloneDeep(hidden);

    saveColumnsToLocalStorage(this.columns, this.selectedServiceId);
  }

  private async fetchService() {
    this.model.loading++;
    try {
      const { data } = await serviceApi.getService(this.selectedServiceId, 'mapping');
      this.selectedServiceMapping = data.mapping;
    } catch (e) {
      if (e instanceof Error) {
        notifyError(e.message);
      }
    } finally {
      this.model.loading--;
    }
  }

  private async fetchDictionaries() {
    if (!(this.selectedServiceMapping
      && this.selectedServiceMapping.attrList.length)) {
      return;
    }
    const dictionaryIds: string[] = [];
    this.selectedServiceMapping.attrList.forEach((attr: any) => {
      if (attr.dictionary) {
        dictionaryIds.push(`id==${attr.dictionary.id}`);
      }
    });
    if (!dictionaryIds.length) {
      return;
    }
    const query = dictionaryIds.join(' or ');
    this.model.loading++;
    try {
      const { data } = await dictionaryApi.searchDictionary(query);
      this.dictionaries = data.map((d: any) => ({
        ...d,
        field: this.selectedServiceMapping.attrList.find(
          (attr: any) => attr.dictionary?.id === d.id
        )?.value
      }));
    } catch (e) {
      if (e instanceof Error) {
        notifyError(e.message);
      }
    } finally {
      this.model.loading--;
    }
  }

  private fillDictionaryValues(records: any) {
    if (!this.dictionaries.length || !records.length) {
      return records;
    }
    const filledRecords = records.map((r: any) => {
      this.dictionaries.forEach((d: any) => {
        const code = '' + get(r, d.field);
        const dictionaryText = d.documents.find((d: any) => d.code === code)?.value;
        set(r, d.field, dictionaryText);
      });
      return r;
    });
    return filledRecords;
  }
}

export interface StatementsSearchQuery {
  savedSearch?: boolean;
  onlyMy: boolean;
  status: string;
  daterange: string;
  daterangeDecision: string;
  employees: string;
  page: number;
  organizations: string;
  departments: string;
  search: string;
  services: string;
  goals: string;
  subjects: string;
  term: number;
}
