import moment from "moment";

export function isPluginCryptoProInstalled()
{
	return new Promise(function(resolve, reject)
	{
		//console.log("isPluginCryptoProInstalled");

		//console.log("window.cadesplugin", "BEFORE", window.cadesplugin);


		setTimeout(() => 
		{
			//console.log("window.cadesplugin", "AFTER", window.cadesplugin);


			if(window.cadesplugin !== undefined)
			{
				window.cadesplugin.async_spawn(function *(args) 
				{
					try 
					{
						yield window.cadesplugin
					} 
					catch (e) 
					{
						console.error("isPluginCryptoProInstalled");
						console.error(e);
		
						reject({ message: e })
						return;
					}
		
					resolve();
				});
			}
			else
			{
				reject();
			}
		}, 500);
	});
}

/**
* Получение списка сертификатов пользователя
* @returns {*}
*/
export function getCertificates()
{
	return new Promise(function(resolve, reject)
	{
		setTimeout(() => 
		{
			window.cadesplugin.async_spawn(function *(args)
			{
				try 
				{
					var store = yield window.cadesplugin.CreateObjectAsync("CAdESCOM.Store");

					yield store.Open(
						window.cadesplugin.CAPICOM_CURRENT_USER_STORE,
						window.cadesplugin.CAPICOM_MY_STORE,
						window.cadesplugin.CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED
					);	

					var certificates = yield store.Certificates;
					var certificatesCount = yield certificates.Count

					var data = [];
					for (let i = 0; i < certificatesCount; i++) 
					{
						let certificate = yield certificates.Item(i+1)

						data.push({
							index: i+1,
							certificate: certificate,
							info: {
								subjectName: parseSubjectNameToObj(yield certificate.SubjectName)['CN'],
								subjectFields: parseSubjectNameToObj(yield certificate.SubjectName), //parseSubjectNameToArray(yield certificate.SubjectName),
								issuerName: yield certificate.IssuerName,
								validFromDate: yield certificate.ValidFromDate,
								validToDate: yield certificate.ValidToDate
							}
						});
					}

					store.Close();
					resolve(data);
				} 
				catch (e) 
				{
					console.error("getCertificates");
					console.error(e);

					reject(e);
				}
			});
		}, 500);
	});
}

/**
* Парсинг данных владельца сертификата
* @param string - Строка с информацией о владельце
* @returns {[{}]}
*
* example:
* input: 'C=RU, S="Магаданская область", L=Магадан, O="OOO ""Несуществующая компания""", OU=Подразделение, CN=Иван Иванов, E=ivan@mail.ru'
* output: [{
*		fieldName: 'Имя',
* 		value: 'Иван Иванов'
* 	}, {
*		fieldName: 'Электронная почта',
*		value: 'ivan@mail.ru'
*  }, {
* 		fieldName: 'Страна',
* 		value: 'RU'
*  }, {
* 		fieldName: 'Область, шташ',
* 		value: '"Магаданская область"'
*  }, {
* 		fieldName: 'Город',
* 		value: 'Магадан'
*  }, {
* 		fieldName: 'Организация',
* 		value: 'OOO ""Несуществующая компания""'
*  }, {
* 		fieldName: 'Подраздиление',
* 		value: 'Подразделение'
*  }]
*
*/

function parseSubjectNameToArray(string) 
{
	//console.log("parseSubjectNameToArray", "string", string);


	let outputData = [];
	let fieldsNames = {
		'CN': 'Имя', 
		'E': 'Электронная почта', 
		'C': 'Страна', 
		'S': 'Область, штат', 
		'L': 'Город', 
		'O': 'Организация', 
		'OU': 'Подраздиление', 
		'SN': 'SN', 
		'G': 'G', 
	};

	// 'C=RU, S="Область, штат"' -> [['C', 'RU'], ['S', '"Область, штат"']]

	let reg = RegExp('[а-яА-Я\\w]*=.*?((?=, [а-яА-Я\\w]+=)|$)','g');
	let fields = string.matchAll(reg);
	fields = Array.from(fields, x => x[0]);
	fields = fields.map(field => field.split('='));

	fields.forEach(field => {
		if (field[0] in fieldsNames) 
		{
			let fieldName = fieldsNames[field[0]];
			outputData.push({
				fieldName: fieldName,
				value: field[1]
			});
		}
	});

	return outputData;
}

/**
* Парсинг данных владельца сертификата
* @param string - Строка с информацией о владельце
* @returns {{}}
*
* example:
* input: 'C=RU, S="Магаданская область", L=Магадан, O="OOO ""Несуществующая компания""", OU=Подразделение, CN=Иван Иванов, E=ivan@mail.ru'
* output: {
*    name: 'Иван Иванов',
*    email: 'ivan@mail.ru',
*    country: 'RU',
*    state: '"Магаданская область"',
*    city: 'Магадан',
*    organization: '"OOO ""Несуществующая компания"""',
*    subdivision: 'Подразделение'
*  }
*
*/

function parseSubjectNameToObj(string) 
{
	const marks = [
		{
			r: new RegExp(/CN\=\"{0,}(.*?)\"{0,},\s{0,}/gm), 
			field: "CN", 
		}, 
		{
			r: new RegExp(/\s{0,}SN\=(.*?)[,\s]|[$]/gm), 
			field: "SN", 
		}, 
		{
			r: new RegExp(/\s{0,}G\=(.*?),\s{0,}/gm), 
			field: "G", 
		}, 
		{
			r: new RegExp(/\s{0,}C\=(.*?)[,\s]|[$]/gm), 
			field: "C", 
		}, 
		{
			r: new RegExp(/\s{0,}S\=(.*?)[,\s]|[$]/gm), 
			field: "S", 
		}, 
		{
			r: new RegExp(/\s{0,}L\=(.*?)[,\s]|[$]/gm), 
			field: "L", 
		}, 
		{
			r: new RegExp(/\s{0,}STREET\=\"(.*?)\"[,\s]|[$]/gm), 
			field: "STREET", 
		}, 
		{
			r: new RegExp(/\s{0,}O\=\"(.*?)\"[,\s]|[$]/gm), 
			field: "O", 
		}, 
		{
			r: new RegExp(/\s{0,}T\=(.*?)[,\s]|[$]/gm), 
			field: "T", 
		}, 
		{
			r: new RegExp(/\s{0,}ОГРН\=(\d+)[,\s]|[$]/gm), 
			field: "ОГРН", 
		}, 
		{
			r: new RegExp(/\s{0,}ОГРНИП\=(\d+)[,\s]|[$]/gm), 
			field: "ОГРНИП", 
		}, 
		{
			r: new RegExp(/\s{0,}СНИЛС\=(\d+)[,\s]|[$]/gm), 
			field: "СНИЛС", 
		}, 
		{
			r: new RegExp(/\s{0,}ИНН\=(\d+)[,\s]|[$]/gm), 
			field: "ИНН", 
		}, 
		{
			r: new RegExp(/\s{0,}E\=(.*?)[,\s]|[$]/gm), 
			field: "E", 
		}, 
		{
			r: new RegExp(/\s{0,}ИНН ЮЛ\=(\d+)[,\s]|[$]/gm), 
			field: "ИНН ЮЛ", 
		}, 
	];

	const fields = {};
	for(let i in marks)
	{
		const chunk = Array.from(`${ string }, `.matchAll(marks[i].r));
		fields[marks[i].field] = chunk[0] !== undefined ? chunk[0][1] : null;
	}

	return fields;
}

/**
* Подпись данных
* @param dataToSign {String} - Данные для подписи
* @param certificate - Сертификат подписи
*
* @param returnObj - RETURN_DATA | RETURN_SIGNATURE
* RETURN_SIGNED_DATA - Возвращает подписанный файл
* RETURN_SIGNATURE - Возвращает подпись
*
* @param signingType - SIGN_ALL_DATA | ADD_SIGNATURE_TO_DATA
* SIGN_ALL_DATA - Подписать данные целиком
* ADD_SIGNATURE_TO_DATA - Добавить подпись к уже подписанным данным
*
* @returns {*}
*/
export function signData(dataToSign, certificate, returnObj, signingType) 
{
	return new Promise(function(resolve, reject) 
	{
		window.cadesplugin.async_spawn(function *(args) 
		{
			try {
				var oSigner = yield window.cadesplugin.CreateObjectAsync("CAdESCOM.CPSigner");
				yield oSigner.propset_Certificate(certificate);

				var oSignedData = yield window.cadesplugin.CreateObjectAsync("CAdESCOM.CadesSignedData");
				var CADES_BES = 1;

				yield oSignedData.propset_ContentEncoding(1); //CADESCOM_BASE64_TO_BINARY
				yield oSignedData.propset_Content(dataToSign);
				yield oSigner.propset_Options(window.cadesplugin.CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY);

				let returnObjFlags = {
					'RETURN_SIGNED_DATA': false,
					'RETURN_SIGNATURE': true
				};

				let returnObjFlag = returnObjFlags[returnObj];
				if (returnObjFlag === undefined) 
				{
					reject("Некорректное значения параметра returnObj. Данный параметр может принимать значения RETURN_SIGNED_DATA или RETURN_SIGNATURE");
					return;
				}

				let signedData = '';
				if (signingType === 'SIGN_ALL_DATA') 
				{
					signedData = yield oSignedData.SignCades(oSigner, CADES_BES, returnObjFlag);
				} 
				else if (signingType === 'ADD_SIGNATURE_TO_DATA') 
				{
					yield oSignedData.VerifyCades(dataToSign, CADES_BES);
					signedData = yield oSignedData.CoSignCades(oSigner, CADES_BES, returnObjFlag);
				} 
				else 
				{
					reject('Incorrect signature type');
				}

				resolve(signedData);
			} catch (e) 
			{
				console.error("window.cadesplugin.async_spawn(function *(args) CATCH");
				console.error(e);
				reject(e);
			}
		});
	});
}

/**
* Проверка наличия у данных подписи
* @param data
* @returns {*} - true | false
* true - данные подписаны
* false - данные не подписаны
*/
export function hasDataSignature(data) 
{
	return new Promise((resolve, reject) => 
	{
		window.cadesplugin.async_spawn(function *(args) 
		{
			try 
			{
				var oSignedData = yield window.cadesplugin.CreateObjectAsync("CAdESCOM.CadesSignedData");
				try 
				{
					let CADES_BES = 1;
					yield oSignedData.VerifyCades(data, CADES_BES);
					resolve(true);
				} 
				catch (e2) 
				{
					console.error("oSignedData.VerifyCades");
					console.error(e2);

					resolve(false);
				}
			} 
			catch (e) 
			{
				console.error("CAdESCOM.CadesSignedData");
				console.error(e);

				reject(e);
			}
		});
	});
}

/**
* Подписание файла
* @param file - Файл для подписи
* @param certificate - Сертификат подписи
*
* @param returnObj - RETURN_DATA | RETURN_SIGNATURE
* RETURN_SIGNED_DATA - Возвращает подписанный файл
* RETURN_SIGNATURE - Возвращает подпись
*
* @returns Подписанный файл
*/
export function signing(file, certificate, returnObj) 
{
	return new Promise((resolve, reject) => 
	{
		// Подписываем файл, у которого нет подписи
		let handlerWithoutSig = (file) => 
		{
			return new Promise((resolve, reject) => 
			{
				var oFReader = new FileReader();
				oFReader.readAsDataURL(file);
				oFReader.onload = (oFREvent) => 
				{
					var header = ";base64,";
					var sFileData = oFREvent.target.result;
					var sBase64Data = sFileData.substr(sFileData.indexOf(header) + header.length);

					signData(sBase64Data, certificate, returnObj, 'SIGN_ALL_DATA').then(resolve, reject);
				}
			});
		}

		// Подписываем файл, у которого уже есть подпись
		let handlerWithSig = (file) => 
		{
			return new Promise((resolve, reject) => 
			{
				file.text()
				.then(text => 
				{
					signData(text, certificate, returnObj, 'ADD_SIGNATURE_TO_DATA').then(resolve, reject);
				})
				.catch(reject);
			});
		}

		file.text()
		.then(text => 
		{
			return hasDataSignature(text)
		})
		.then(result => 
		{
			if (result) 
			{
				return handlerWithSig(file)
			} 
			else 
			{
				return handlerWithoutSig(file)
			}
		})
		.then(resolve).catch(reject);
	});
}

/**
* Подпись файла и вернуть подписанный файл
* @param file - Файл для подписи
* @param certificate - Сертификат подписи
* @returns Подписанный файл
*/
export function signFile(file, certificate) 
{
	//console.log("utils", "digital_signature", "signFile", file, certificate);


	return new Promise((resolve, reject) => 
	{
		signing(file, certificate, 'RETURN_SIGNED_DATA')
		.then(signedData => 
		{
			let filename = file.name + '.sig';
			let signedFile = new File([signedData], filename);

			resolve(signedFile);
		})
		.catch(reject);
	});
}

/**
* Подписать файл и вернуть файл с данными подписи
* @param file - Файл для подписи
* @param certificate - Сертификат подписи
* @returns Подписанный файл
*/
export function generateSignature(file, certificate) 
{
	//console.log("utils", "digital_signature", "generateSignature", file, certificate);


	return new Promise((resolve, reject) => 
	{
		signing(file, certificate, 'RETURN_SIGNATURE')
		.then(signedData => 
		{
			let filename = file.name + '.sig';
			let signedFile = new File([signedData], filename);

			resolve(signedFile);
		})
		.catch(reject);
	});
}