import ko from 'ko';

import { pString, defaultOptionsAfterRender } from 'Common/Utils';

import { Scope } from 'Common/Enums';
import { i18n } from 'Common/Translator';

import { PgpUserStore } from 'Stores/User/Pgp';

import { EmailModel } from 'Model/Email';

import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';

const KEY_NAME_SUBSTR = -8,
	i18nPGP = (key, params) => i18n('PGP_NOTIFICATIONS/' + key, params);

class ComposeOpenPgpPopupView extends AbstractViewPopup {
	constructor() {
		super('ComposeOpenPgp');

		this.publicKeysOptionsCaption = i18nPGP('ADD_A_PUBLICK_KEY');
		this.privateKeysOptionsCaption = i18nPGP('SELECT_A_PRIVATE_KEY');

		this.addObservables({
			notification: '',

			sign: false,
			encrypt: false,

			password: '',

			text: '',
			selectedPrivateKey: null,
			selectedPublicKey: null,

			signKey: null,

			submitRequest: false
		});
		this.encryptKeys = ko.observableArray();

		this.addComputables({
			encryptKeysView:  () => this.encryptKeys.map(oKey => (oKey ? oKey.key : null)).filter(v => v),

			privateKeysOptions: () => {
				const opts = PgpUserStore.openpgpkeysPrivate().map(oKey => {
					if (this.signKey() && this.signKey().key.id === oKey.id) {
						return null;
					}
					return oKey.users.map(user => ({
						id: oKey.guid,
						name: '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user,
						key: oKey
					}));
				});

				return opts.flat().filter(v => v);
			},

			publicKeysOptions: () => {
				const opts = PgpUserStore.openpgpkeysPublic().map(oKey => {
					if (this.encryptKeysView().includes(oKey)) {
						return null;
					}
					return oKey.users.map(user => ({
						id: oKey.guid,
						name: '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user,
						key: oKey
					}));
				});
				return opts.flat().filter(v => v);
			}
		});

		this.resultCallback = null;

		this.selectedPrivateKey.subscribe((value) => {
			if (value) {
				this.selectCommand();
				this.updateCommand();
			}
		});

		this.selectedPublicKey.subscribe((value) => {
			if (value) {
				this.addCommand();
			}
		});

		this.defaultOptionsAfterRender = defaultOptionsAfterRender;

		this.deletePublickKey = this.deletePublickKey.bind(this);

		decorateKoCommands(this, {
			doCommand: self => !self.submitRequest() && (self.sign() || self.encrypt()),
			selectCommand: 1,
			addCommand: 1,
			updateCommand: 1,
		});
	}

	doCommand() {
		let result = true,
			privateKey = null,
			aPublicKeys = [];

		this.submitRequest(true);

		if (result && this.sign()) {
			if (!this.signKey()) {
				this.notification(i18nPGP('NO_PRIVATE_KEY_FOUND'));
				result = false;
			} else if (!this.signKey().key) {
				this.notification(
					i18nPGP('NO_PRIVATE_KEY_FOUND_FOR', {
						EMAIL: this.signKey().email
					})
				);

				result = false;
			}

			if (result) {
				const privateKeys = this.signKey().key.getNativeKeys();
				privateKey = privateKeys[0] || null;

				try {
					if (privateKey) {
						privateKey.decrypt(pString(this.password()));
					}
				} catch (e) {
					privateKey = null;
				}

				if (!privateKey) {
					this.notification(i18nPGP('NO_PRIVATE_KEY_FOUND'));
					result = false;
				}
			}
		}

		if (result && this.encrypt()) {
			if (this.encryptKeys.length) {
				aPublicKeys = [];

				this.encryptKeys.forEach(oKey => {
					if (oKey && oKey.key) {
						aPublicKeys = aPublicKeys.concat(oKey.key.getNativeKeys().flat(Infinity).filter(v => v));
					} else if (oKey && oKey.email) {
						this.notification(
							i18nPGP('NO_PUBLIC_KEYS_FOUND_FOR', {
								EMAIL: oKey.email
							})
						);

						result = false;
					}
				});

				if (result && (!aPublicKeys.length || this.encryptKeys.length !== aPublicKeys.length)) {
					result = false;
				}
			} else {
				this.notification(i18nPGP('NO_PUBLIC_KEYS_FOUND'));
				result = false;
			}
		}

		if (result && this.resultCallback) {
			setTimeout(() => {
				let pgpPromise = null;

				try {
					if (aPublicKeys.length) {
						if (privateKey) {
							pgpPromise = PgpUserStore.openpgp.encrypt({
								data: this.text(),
								publicKeys: aPublicKeys,
								privateKeys: [privateKey]
							});
						} else {
							pgpPromise = PgpUserStore.openpgp.encrypt({
								data: this.text(),
								publicKeys: aPublicKeys
							});
						}
					} else if (privateKey) {
						pgpPromise = PgpUserStore.openpgp.sign({
							data: this.text(),
							privateKeys: [privateKey]
						});
					}
				} catch (e) {
					console.log(e);

					this.notification(
						i18nPGP('PGP_ERROR', {
							ERROR: '' + e
						})
					);
				}

				if (pgpPromise) {
					try {
						pgpPromise
							.then((mData) => {
								this.resultCallback(mData.data);
								this.cancelCommand();
							})
							.catch((e) => {
								this.notification(
									i18nPGP('PGP_ERROR', {
										ERROR: '' + e
									})
								);
							});
					} catch (e) {
						this.notification(
							i18nPGP('PGP_ERROR', {ERROR: '' + e})
						);
					}
				}

				this.submitRequest(false);
			}, 20);
		} else {
			this.submitRequest(false);
		}

		return result;
	}

	selectCommand() {
		const keyId = this.selectedPrivateKey(),
			option = keyId ? this.privateKeysOptions().find(item => item && keyId === item.id) : null;

		if (option) {
			this.signKey({
				empty: !option.key,
				selected: ko.observable(!!option.key),
				users: option.key.users,
				hash: option.key.id.substr(KEY_NAME_SUBSTR).toUpperCase(),
				key: option.key
			});
		}
	}

	addCommand() {
		const keyId = this.selectedPublicKey(),
			option = keyId ? this.publicKeysOptions().find(item => item && keyId === item.id) : null;

		if (option) {
			this.encryptKeys.push({
				empty: !option.key,
				selected: ko.observable(!!option.key),
				removable: ko.observable(!this.sign() || !this.signKey() || this.signKey().key.id !== option.key.id),
				users: option.key.users,
				hash: option.key.id.substr(KEY_NAME_SUBSTR).toUpperCase(),
				key: option.key
			});
		}
	}

	updateCommand() {
		this.encryptKeys.forEach(oKey =>
			oKey.removable(!this.sign() || !this.signKey() || this.signKey().key.id !== oKey.key.id)
		);
	}

	deletePublickKey(publicKey) {
		this.encryptKeys.remove(publicKey);
	}

	clearPopup() {
		this.notification('');

		this.sign(false);
		this.encrypt(false);

		this.password('');

		this.signKey(null);
		this.encryptKeys([]);
		this.text('');

		this.resultCallback = null;
	}

	onBuild() {
//		shortcuts.add('tab', 'shift', Scope.ComposeOpenPgp, () => {
		shortcuts.add('tab', '', Scope.ComposeOpenPgp, () => {
			let btn = this.querySelector('.inputPassword');
			if (btn.matches(':focus')) {
				btn = this.querySelector('.buttonDo');
			}
			btn.focus();
			return false;
		});
	}

	onHideWithDelay() {
		this.clearPopup();
	}

	onShowWithDelay() {
		this.querySelector(this.sign() ? '.inputPassword' : '.buttonDo').focus();
	}

	onShow(fCallback, sText, identity, sTo, sCc, sBcc) {
		this.clearPopup();

		let rec = [],
			emailLine = '';

		const email = new EmailModel();

		this.resultCallback = fCallback;

		if (sTo) {
			rec.push(sTo);
		}

		if (sCc) {
			rec.push(sCc);
		}

		if (sBcc) {
			rec.push(sBcc);
		}

		rec = rec.join(', ').split(',');
		rec = rec.map(value => {
				email.clear();
				email.parse(value.trim());
				return email.email || false;
			}).filter(v => v);

		if (identity && identity.email()) {
			emailLine = identity.email();
			rec.unshift(emailLine);

			const keys = PgpUserStore.findAllPrivateKeysByEmailNotNative(emailLine);
			if (keys && keys[0]) {
				this.signKey({
					users: keys[0].users || [emailLine],
					hash: keys[0].id.substr(KEY_NAME_SUBSTR).toUpperCase(),
					key: keys[0]
				});
			}
		}

		if (this.signKey()) {
			this.sign(true);
		}

		if (rec.length) {
			this.encryptKeys(
				rec.map(recEmail => {
					const keys = PgpUserStore.findAllPublicKeysByEmailNotNative(recEmail);
					return keys
						? keys.map(publicKey => ({
								empty: !publicKey,
								selected: ko.observable(!!publicKey),
								removable: ko.observable(
									!this.sign() || !this.signKey() || this.signKey().key.id !== publicKey.id
								),
								users: publicKey ? publicKey.users || [recEmail] : [recEmail],
								hash: publicKey ? publicKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() : '',
								key: publicKey
						  }))
						: [];
				}).flat().validUnique(encryptKey => encryptKey.hash)
			);

			if (this.encryptKeys.length) {
				this.encrypt(true);
			}
		}

		this.text(sText);
	}
}

export { ComposeOpenPgpPopupView, ComposeOpenPgpPopupView as default };
