import {
  createDictionaryMapFromSchema,
  setValuesFromDictionaries,
} from '@utils/dict-map-utils';
import get from 'lodash/get';
import { Schema } from '@devsun/eva-models';
import { i18n } from '@plugins/i18n';
import store from '@state/store';
import { fetchAttachment } from '@services/registry-service';
import axios from 'axios';
import PrinterService from '@services/printer/index';
import { convertDateFormat, DateFormat } from '@utils/format-date';
import { Certificate } from 'crypto-pro';
import { parseCertificatePairs } from '@/utils/crypto-pro/helpers';
import set from 'lodash/set';
import cloneDeep from 'lodash/cloneDeep';
import { ServiceFileNameFactory } from '@utils/file/ServiceFileNameFactory';

export const PRINT_APPLICATION = 'printApplication';
export const RECEPTION_PRINT_APPLICATION = 'ReceptionTemplate';

async function prepareStatementData(
  schema: any,
  authorSchema: any,
  model: any,
  serviceCard: any,
  templateAsBase64 = false,
  documentName?: string
) {
  let localModel = cloneDeep(model);
  localModel = addMfcText(localModel);
  localModel = addReservationText(localModel);

  const document = serviceCard?.documents?.find(
    (doc: any) =>
      doc.source === PRINT_APPLICATION &&
      (!('goal' in doc) || doc?.goal === model.statement?.goal)
  );

  if (document) {
    return await prepateWithinTemplate(
      localModel,
      serviceCard,
      document,
      templateAsBase64,
      documentName
    );
  } else {
    return await prepateWithoutTemplate(
      schema,
      authorSchema,
      localModel,
      serviceCard,
      templateAsBase64
    );
  }
}

function findPath(ob: { [key: string]: any }, key: string): string {
  const path: string[] = [];

  const keyExists = (obj: { [key: string]: any }): boolean => {
    if (!obj || (typeof obj !== "object" && !Array.isArray(obj))) {
      return false;
    }
    else if (obj.hasOwnProperty(key)) {
      return true;
    }
    else if (Array.isArray(obj)) {
      let parentKey = path.length ? path.pop() : "";

      for (let i = 0; i < obj.length; i++) {
        path.push(`${parentKey}[${i}]`);
        const result = keyExists(obj[i]);
        if (result) {
          return result;
        }
        path.pop();
      }
    }
    else {
      for (const k in obj) {
        path.push(k);
        const result = keyExists(obj[k]);
        if (result) {
          return result;
        }
        path.pop();
      }
    }
    return false;
  };

  keyExists(ob);

  if (!path.length && ob[key]) {
    return key;
  }

  return path.length ? (path.join(".") + '.' + key) : '';
}

function joinPropertyValues(value: any, resultProperties: string[]) {
  const arr = resultProperties.map((p: string) => value[p]);
  return arr.join(', ');
}

function addMfcText(localModel: any): any {
  const mfcPath = findPath(localModel, 'Mfc');
  if (mfcPath) {
    const mfcValue = get(localModel, mfcPath);
    const printValue = joinPropertyValues(mfcValue, ['title', 'address', 'ogrn']);
    set(localModel, mfcPath, printValue);
  }
  return localModel;
}

function addReservationText(localModel: any): any {
  const reservationPath = findPath(localModel, 'reservation');
  if (reservationPath) {
    const reservationValue = get(localModel, reservationPath);
    const { room, date, timeFrom, timeTo } = reservationValue?.textData;
    const printValue = `${room}, ${date}, `+
      `c ${timeFrom} по ${timeTo}`;
    set(localModel, reservationPath, printValue);
  }
  return localModel;
}

async function downloadStatement(
  schema: any,
  authorSchema: any,
  model: any,
  serviceCard: any,
  templateAsBase64 = false,
  certificate?: Certificate | null
) {
  let localModel = cloneDeep(model);
  localModel = addMfcText(localModel);
  localModel = addReservationText(localModel);

  const document = serviceCard?.documents?.find(
    (doc: any) =>
      doc.source === RECEPTION_PRINT_APPLICATION
  );

  let departmentName = get(localModel, 'system.department.fullName');
  if (!departmentName) {
    let departmentPath = findPath(localModel, 'DepartmentId')?.slice(0, -13);
    departmentPath = departmentPath ? `${departmentPath}.DepartmentName` : 'DepartmentName';
    departmentName = store.getters['formGenerator/data']?.organization?.fullname;
    set(localModel, departmentPath, departmentName);
  }
  set(localModel, 'departmentName', departmentName);

  let result;
  if (document) {
    const documentName = ServiceFileNameFactory.create(localModel, document);
    result = await prepateWithinTemplate(
      localModel,
      serviceCard,
      document,
      templateAsBase64,
      documentName,
      true,
    );
  } else {
    result = await prepateWithoutTemplate(
      schema,
      authorSchema,
      localModel,
      serviceCard,
      templateAsBase64,
      certificate,
      true,
    );
  }
  PrinterService.fetchConvertedFileSync(result);
}

const schemaName = 'ServiceDocument';
const id = 'b95877a1-42dd-4e98-aa21-c3f4ac33353c';
const templateFilename = 'template_TemplatePersonalInfo.docx';

async function downloadAgreement(model: any): Promise<Blob | string> {
  const url = await fetchAttachment(schemaName, id, templateFilename);

  const { data: blob } = await axios.get(url, {
    responseType: 'arraybuffer',
  });

  const file = new File([blob], `Согласие на обработку персональных данных`);
  return await PrinterService.fetchConvertedFileSync({
    input: 'DOCX',
    output: 'PDF',
    template: file,
    parameters: JSON.stringify(model),
    fileName: 'Согласие на обработку персональных данных',
    type:
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  });
}

async function stampPdf(document: File, certificate: any): Promise<Blob> {
  return await PrinterService.fetchFileSyncWithStamp({
    certificate,
    document,
    parameters: JSON.stringify({ todo: 'allowEmptyParams' }),
    download: false,
  });
}

async function prepateWithinTemplate(
  model: any,
  serviceCard: any,
  document: any,
  templateAsBase64: boolean,
  documentName?: string,
  toPdf?: boolean,
) {
  const { templateUrl, schema: schemaName, id: uuid } = document;
  let bin;
  const resultExtension = toPdf ? 'pdf' : 'docx';
  const resultFileName =
    documentName ??
    `${String(i18n.t('registries.objectCard.statement'))} ${
      serviceCard?.numberPrefix || model?.service
    }.${resultExtension}`;
  if (!templateAsBase64) {
    const url = await fetchAttachment(schemaName, uuid, templateUrl);
    const { data: blob } = await axios.get(url, {
      responseType: 'arraybuffer',
    });
    bin = new File([blob], `${resultFileName}`);
  }

  // @ts-ignore
  const schema: Partial<Schema.MetaClassSchemaRaw> = store.getters[
    'utils/cards/getSchemaByPath'
  ]('data');

  if (!model.dictionaries) {
    model.dictionaries = {};
  }
  const dictionaryMap = {
    ...createDictionaryMapFromSchema(schema),
    'common.delegateType': model.dictionaries.userType,
    'delegate.gender': model.dictionaries.gender,
    'delegate.documents.type': model.dictionaries.document,
    'common.userType': model.dictionaries.userType,
    'applicant.gender': model.dictionaries.gender,
    'applicant.documents.type': model.dictionaries.document,
    'representative.gender': model.dictionaries.gender,
    'representative.documents.type': model.dictionaries.document,
    ...schema.dictionaries,
  };

  const resultingModel = await setValuesFromDictionaries(model, dictionaryMap);

  return {
    input: 'DOCX',
    output: resultExtension.toUpperCase(),
    template: templateAsBase64 ? null : bin,
    ...(templateAsBase64 && {
      templateId: templateUrl,
      documentId: uuid,
    }),
    parameters: JSON.stringify(resultingModel),
    fileName: resultFileName,
    type:
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  };
}

function getDate(date: string): string {
  if (!date) {
    return '';
  }
  return convertDateFormat(date, DateFormat.invertedDate, DateFormat.date) || (date);
}

function getSignatureOwner(subjectName: string): string {
  if (!subjectName) {
    return '';
  }
  const names = ['SN', 'G', 'T'];
  const pairs = parseCertificatePairs(subjectName);
  const signatureOwner: { [k: string]: string } = {
    'SN': '',
    'G': '',
    'T': ''
  };
  pairs.forEach((p: any) => {
    if (names.includes(p[0])) {
      signatureOwner[p[0]] = p[1];
    }
  })
  return `${signatureOwner.T} ${signatureOwner.SN} ${signatureOwner.G}`;
}

async function prepateWithoutTemplate(
  schema: any,
  authorSchema: any,
  model: any,
  serviceCard: any,
  templateAsBase64: boolean,
  certificate?: any,
  isCreateStatementPage?: boolean
) {
  const templates = [
    `<div class="q-gutter-y-md row bg-white q-pb-md q-pt-sm q-mt-md rounded-borders">
          <div class="text-subtitle1" style="font-size: 1.2rem; text-align:center;">
              <strong>${get(model, 'system.fullName', '')}</strong>
          </div>
          <div class="q-gutter-y-md" style="text-align: right; margin-top: 8px;">
              <div>
                  <span>Дата подачи:</span>
                  <span class="text-primary">${get(
                    model,
                    'system.startDate',
                    ''
                  ) || getDate(get(model, 'currentDate', ''))}</span>
              </div>
              <div class="q-mb-lg">
                  <span>№</span>
                  <span class="text-primary">${get(
                    model,
                    'common.orderId',
                    ''
                  )}</span>
              </div>
              <div>
                  <div class="text-primary">
                    ${ get(model, 'system.department.fullName', '') ||
                    get(model, 'departmentName', '') }
                  </div>
                  <div style="font-size: .7rem; text-decoration: overline;">
                      (Наименование органа, уполномоченного на предоставление услуги)
                  </div>
              </div>
          </div>
      </div>`,
  ];
/*   const footers = [
    `<div class="row q-mt-xl q-pt-xl footer">
      <div class="col q-pl-md footer-block">
        ${getDate(model.currentDate || model.Service?.currentDate)}
      </div>
      <div class="col text-right q-pr-md footer-block">
        ${getSignatureOwner(certificate?.data?.subjectName || certificate?.value?.subjectName)}
      </div>
    </div>
    `
  ]; */
  const footers = [
    `<table cellspacing="0" class="printer-table">
      <tbody>
        <tr>
          <th>&#160;</th><th>&#160;</th><th>&#160;</th><th>&#160;</th>
        </tr>
        <tr>
          <td>
            <div>_____________</div>
            <div>(подпись)</div>
          </td>
          <td>
            <div>_______________________________</div>
            <div>(расшифровка подписи)</div>
          </td>
          <td>
            <div>"__" _______________ 202_г.</div>
            <div>&#160;</div>
          </td>
        </tr>
      </tbody>
    </table>`
  ];

  // @ts-ignore
  const template = await store.dispatch(
    'utils/components/prepareStatementPrintTemplate',
    {
      schema,
      authorSchema,
      model,
      templates,
      ...(isCreateStatementPage && { footers }),
    }
  );

  const fileName = `${i18n.t('registries.objectCard.statement')} ${
    serviceCard?.numberPrefix || model.service
  }`;

  if (templateAsBase64) {
    return {
      template,
      parameters: JSON.stringify({ todo: 'allowEmptyParams' }),
      fileName: `${fileName}.pdf`,
    };
  } else {
    const blob = new Blob([template], { type: 'text/html' });
    const bin = new File([blob], `${fileName}.html`);

    return {
      template: bin,
      parameters: JSON.stringify({ todo: 'allowEmptyParams' }),
      fileName: `${fileName}.pdf`,
    };
  }
}

export { prepareStatementData, downloadStatement, downloadAgreement, stampPdf };
