import {
  getCertificate,
  getUserCertificates,
  isValidSystemSetup,
  createHash,
  createDetachedSignature,
} from 'crypto-pro';
import { ActionTree, mapActions } from 'vuex';
import get from 'lodash/get';
import isFunction from 'lodash/isFunction';
import { isEmpty } from 'lodash';

const CADESCOM_CADES_BES = 1;
const CADESCOM_BASE64_TO_BINARY = 1;
const CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256 = 101;
const hashAlg = CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256; // ГОСТ Р 34.11-2012

function InitializeHashedData(hashAlg: number, sHashValue: number) {
  return new Promise((resolve, reject) => {
    // @ts-ignore
    window.cadesplugin.async_spawn(function* () {
      // @ts-ignore
      const oHashedData = yield window.cadesplugin.CreateObjectAsync(
        'CAdESCOM.HashedData'
      );

      oHashedData.propset_Algorithm(hashAlg);
      oHashedData.SetHashValue(sHashValue);

      return resolve(oHashedData);
    });
  });
}

export const state = {};

export const getters = {};

export const mutations = {};

export const actions: ActionTree<any, any> = {
  fetchCadesProp(store, { certificate, propName }) {
    return new Promise((resolve, reject) => {
      certificate
        .getCadesProp(propName)
        .then((prop: any) => {
          return resolve(prop);
        })
        .catch((e: Error) => {
          return reject(e);
        });
    });
  },
  fetchCertificate(store, { thumbprint }) {
    return new Promise((resolve, reject) => {
      getCertificate(thumbprint)
        .then((certificate) => {
          return resolve(certificate);
        })
        .catch((e) => {
          return reject(e);
        });
    });
  },
  fetchCertificates() {
    return new Promise((resolve, reject) => {
      getUserCertificates()
        .then((certificates) => {
          return resolve(certificates);
        })
        .catch((e) => {
          return reject(e);
        });
    });
  },
  generateBase64ByCertificate(store, { certificate }) {
    return new Promise((resolve, reject) => {
      certificate
        .exportBase64()
        .then((base64: string) => {
          return resolve(base64.replace(/(?:\r\n|\r|\n)/g, ''));
        })
        .catch((e: Error) => {
          return reject(e);
        });
    });
  },
  createSignature({ dispatch }, { hash, certificate }) {
    return new Promise((resolve, reject) => {
      createDetachedSignature(certificate.thumbprint, hash)
        .then((signature) => {
          resolve(signature);
        })
        .catch((e) => {
          reject(e);
        });
    });
  },
  verifySignature(store, { hash, signature }) {
    return new Promise((resolve, reject) => {
      // @ts-ignore
      window.cadesplugin.async_spawn(function* () {
        // @ts-ignore
        const oSignedData = yield window.cadesplugin.CreateObjectAsync(
          'CAdESCOM.CadesSignedData'
        );

        InitializeHashedData(hashAlg, hash).then((oHashedData) => {
          try {
            const verification = oSignedData.VerifyHash(
              oHashedData,
              signature,
              CADESCOM_CADES_BES
            );

            verification
              .then(() => resolve(true))
              .catch((error: Error) => {
                console.error(
                  '[crypto-prop][verify signature] Verification failed',
                  error,
                  { hash, hashAlg, signature, oHashedData }
                );
                reject(error);
              });
          } catch (error) {
            console.error('[crypto-pro][verify signature] error', error);

            reject(error);
          }
        });
      });
    });
  },
  generateHash(store, { file }) {
    return new Promise(function (resolve, reject) {
      const reader: any = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => {
        const dataInBase64 = reader.result.split(',')[1];
        createCadesHash(dataInBase64)
          .then((hash) => {
            return resolve(hash);
          })
          .catch((e) => {
            reject(e);
          });
      };
    });
  },
  isValidSystemCertificate(store, certificate) {
    return new Promise((resolve, reject) => {
      if (isFunction(get(certificate, 'isValid'))) {
        certificate
          .isValid()
          .then((isValid: boolean) => {
            resolve(isValid);
          })
          .catch((e: Error) => {
            reject(e);
          });
      } else {
        reject(new Error('Certificate has not isValid method'));
      }
    });
  },
  isValidSystemSetup() {
    return new Promise((resolve, reject) => {
      isValidSystemSetup()
        .then((isValid) => {
          return resolve(isValid);
        })
        .catch((e) => {
          return reject(e);
        });
    });
  },
};

declare var cadesplugin: {
  async_spawn(args: any): string
  CreateObjectAsync(args: any): Yield,
}
type Yield = {}
type GeneratorReturn = void
type GeneratorAccept = {
  propset_Algorithm(arg: number): Yield,
  propset_DataEncoding(arg: number): Yield,
  Hash(arg:string):Yield,
  Value: Yield
}

function createCadesHash(dataInBase64: any) {
  return new Promise((resolve, reject) => {    

    if(!cadesplugin) throw console.error('install cades plugin');

    cadesplugin.async_spawn(function* (): Generator<
      Yield,
      GeneratorReturn,
      GeneratorAccept
    >{
      const oHashedData = yield cadesplugin.CreateObjectAsync("CAdESCOM.HashedData");
      if (isEmpty(oHashedData)) {
        throw console.error('oHashedData is empty');
      } 
      yield oHashedData.propset_Algorithm(CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256);
      yield oHashedData.propset_DataEncoding(CADESCOM_BASE64_TO_BINARY);
      yield oHashedData.Hash(dataInBase64);

      const sHashValue = yield oHashedData.Value
      return resolve(sHashValue);
    });
  });
}
export const utilsCryptoProMethods = mapActions('utils/cryptoPro', [
  'createSignature',
  'verifySignature',
  'fetchCadesProp',
  'fetchCertificates',
  'generateBase64ByCertificate',
  'generateHash',
  'isValidSystemCertificate',
  'isValidSystemSetup',
]);
