
import window from 'window';
import _ from '_';
import $ from '$';
import ko from 'ko';
import key from 'key';
import Jua from 'Jua';

import {
	SaveSettingsStep, ContactPropertyType, ComposeType,
	Capa, Magics, StorageResultType, Notification, KeyState
} from 'Common/Enums';

import {
	delegateRunOnDestroy, computedPagenatorHelper,
	inArray, trim, windowResizeCallback,
	isNonEmptyArray, fakeMd5, pInt, isUnd
} from 'Common/Utils';

import {CONTACTS_PER_PAGE} from 'Common/Consts';
import {bMobileDevice} from 'Common/Globals';

import {Selector} from 'Common/Selector';
import {exportContactsVcf, exportContactsCsv, uploadContacts} from 'Common/Links';
import {i18n, getNotification} from 'Common/Translator';

import SettingsStore from 'Stores/User/Settings';
import ContactStore from 'Stores/User/Contact';

import Remote from 'Remote/User/Ajax';

import * as Settings from 'Storage/Settings';

import {EmailModel} from 'Model/Email';
import {ContactModel} from 'Model/Contact';
import {ContactPropertyModel} from 'Model/ContactProperty';

import {getApp} from 'Helper/Apps/User';

import {popup, command, showScreenPopup, hideScreenPopup, routeOn, routeOff} from 'Knoin/Knoin';
import {AbstractViewNext} from 'Knoin/AbstractViewNext';

@popup({
	name: 'View/Popup/Contacts',
	templateID: 'PopupsContacts'
})
class ContactsPopupView extends AbstractViewNext
{
	constructor() {
		super();

		const
			fFastClearEmptyListHelper = (list) => {
				if (list && 0 < list.length) {
					this.viewProperties.removeAll(list);
					delegateRunOnDestroy(list);
				}
			};

		this.bBackToCompose = false;
		this.sLastComposeFocusedField = '';

		this.allowContactsSync = ContactStore.allowContactsSync;
		this.enableContactsSync = ContactStore.enableContactsSync;
		this.allowExport = !bMobileDevice;

		this.search = ko.observable('');
		this.contactsCount = ko.observable(0);
		this.contacts = ContactStore.contacts;

		this.currentContact = ko.observable(null);

		this.importUploaderButton = ko.observable(null);

		this.contactsPage = ko.observable(1);
		this.contactsPageCount = ko.computed(() => {
			const iPage = window.Math.ceil(this.contactsCount() / CONTACTS_PER_PAGE);
			return 0 >= iPage ? 1 : iPage;
		});

		this.contactsPagenator = ko.computed(computedPagenatorHelper(this.contactsPage, this.contactsPageCount));

		this.emptySelection = ko.observable(true);
		this.viewClearSearch = ko.observable(false);

		this.viewID = ko.observable('');
		this.viewReadOnly = ko.observable(false);
		this.viewProperties = ko.observableArray([]);

		this.viewSaveTrigger = ko.observable(SaveSettingsStep.Idle);

		this.viewPropertiesNames = this.viewProperties.filter(
			(property) => -1 < inArray(property.type(), [ContactPropertyType.FirstName, ContactPropertyType.LastName])
		);

		this.viewPropertiesOther = this.viewProperties.filter(
			(property) => -1 < inArray(property.type(), [ContactPropertyType.Note])
		);

		this.viewPropertiesOther = ko.computed(() => {
			const list = _.filter(this.viewProperties(),
				(property) => -1 < inArray(property.type(), [ContactPropertyType.Nick])
			);
			return _.sortBy(list, (property) => property.type());
		});

		this.viewPropertiesEmails = this.viewProperties.filter(
			(property) => ContactPropertyType.Email === property.type()
		);

		this.viewPropertiesWeb = this.viewProperties.filter(
			(property) => ContactPropertyType.Web === property.type()
		);

		this.viewHasNonEmptyRequaredProperties = ko.computed(() => {
			const
				names = this.viewPropertiesNames(),
				emails = this.viewPropertiesEmails(),
				fFilter = (property) => '' !== trim(property.value());

			return !!(_.find(names, fFilter) || _.find(emails, fFilter));
		});

		this.viewPropertiesPhones = this.viewProperties.filter(
			(property) => ContactPropertyType.Phone === property.type()
		);

		this.viewPropertiesEmailsNonEmpty = this.viewPropertiesNames.filter(
			(property) => '' !== trim(property.value())
		);

		this.viewPropertiesEmailsEmptyAndOnFocused = this.viewPropertiesEmails.filter((property) => {
			const foc = property.focused();
			return '' === trim(property.value()) && !foc;
		});

		this.viewPropertiesPhonesEmptyAndOnFocused = this.viewPropertiesPhones.filter((property) => {
			const foc = property.focused();
			return '' === trim(property.value()) && !foc;
		});

		this.viewPropertiesWebEmptyAndOnFocused = this.viewPropertiesWeb.filter((property) => {
			const foc = property.focused();
			return '' === trim(property.value()) && !foc;
		});

		this.viewPropertiesOtherEmptyAndOnFocused = ko.computed(
			() => _.filter(this.viewPropertiesOther(), (property) => {
				const foc = property.focused();
				return '' === trim(property.value()) && !foc;
			})
		);

		this.viewPropertiesEmailsEmptyAndOnFocused.subscribe((list) => {
			fFastClearEmptyListHelper(list);
		});

		this.viewPropertiesPhonesEmptyAndOnFocused.subscribe((list) => {
			fFastClearEmptyListHelper(list);
		});

		this.viewPropertiesWebEmptyAndOnFocused.subscribe((list) => {
			fFastClearEmptyListHelper(list);
		});

		this.viewPropertiesOtherEmptyAndOnFocused.subscribe((list) => {
			fFastClearEmptyListHelper(list);
		});

		this.viewSaving = ko.observable(false);

		this.useCheckboxesInList = SettingsStore.useCheckboxesInList;

		this.search.subscribe(() => {
			this.reloadContactList();
		});

		this.contacts.subscribe(windowResizeCallback);
		this.viewProperties.subscribe(windowResizeCallback);

		this.contactsChecked = ko.computed(
			() => _.filter(this.contacts(), (item) => item.checked())
		);

		this.contactsCheckedOrSelected = ko.computed(() => {
			const
				checked = this.contactsChecked(),
				selected = this.currentContact();

			return _.union(checked, selected ? [selected] : []);
		});

		this.contactsCheckedOrSelectedUids = ko.computed(
			() => _.map(this.contactsCheckedOrSelected(), (contact) => contact.idContact)
		);

		this.selector = new Selector(
			this.contacts, this.currentContact, null,
			'.e-contact-item .actionHandle', '.e-contact-item.selected',
			'.e-contact-item .checkboxItem', '.e-contact-item.focused');

		this.selector.on('onItemSelect', (contact) => {
			this.populateViewContact(contact ? contact : null);
			if (!contact)
			{
				this.emptySelection(true);
			}
		});

		this.selector.on('onItemGetUid', (contact) => (contact ? contact.generateUid() : ''));

		this.bDropPageAfterDelete = false;

		this.watchDirty = ko.observable(false);
		this.watchHash = ko.observable(false);

		this.viewHash = ko.computed(() => '' + _.map(this.viewProperties(), (oItem) => oItem.value()).join(''));

		// this.saveCommandDebounce = _.debounce(_.bind(this.saveCommand, this), 1000);

		this.viewHash.subscribe(() => {
			if (this.watchHash() && !this.viewReadOnly() && !this.watchDirty())
			{
				this.watchDirty(true);
			}
		});

		this.sDefaultKeyScope = KeyState.ContactList;
	}

	@command()
	newCommand() {
		this.populateViewContact(null);
		this.currentContact(null);
	}

	@command((self) => 0 < self.contactsCheckedOrSelected().length)
	deleteCommand() {
		this.deleteSelectedContacts();
		this.emptySelection(true);
	}

	@command((self) => 0 < self.contactsCheckedOrSelected().length)
	newMessageCommand() {

		if (!Settings.capa(Capa.Composer))
		{
			return false;
		}

		let
			aE = [],
			toEmails = null,
			ccEmails = null,
			bccEmails = null;

		const aC = this.contactsCheckedOrSelected();
		if (isNonEmptyArray(aC))
		{
			aE = _.map(aC, (oItem) => {
				if (oItem)
				{
					const
						data = oItem.getNameAndEmailHelper(),
						email = data ? new EmailModel(data[0], data[1]) : null;

					if (email && email.validate())
					{
						return email;
					}
				}

				return null;
			});

			aE = _.compact(aE);
		}

		if (isNonEmptyArray(aE))
		{
			this.bBackToCompose = false;

			hideScreenPopup(ContactsPopupView);

			switch (this.sLastComposeFocusedField)
			{
				case 'cc':
					ccEmails = aE;
					break;
				case 'bcc':
					bccEmails = aE;
					break;
				case 'to':
				default:
					toEmails = aE;
					break;
			}

			this.sLastComposeFocusedField = '';

			_.delay(() => {
				showScreenPopup(require('View/Popup/Compose'), [ComposeType.Empty, null, toEmails, ccEmails, bccEmails]);
			}, Magics.Time200ms);
		}

		return true;
	}

	@command()
	clearCommand() {
		this.search('');
	}

	@command((self) => {
		const
			bV = self.viewHasNonEmptyRequaredProperties(),
			bReadOnly = self.viewReadOnly();
		return !self.viewSaving() && bV && !bReadOnly;
	})
	saveCommand() {

		this.viewSaving(true);
		this.viewSaveTrigger(SaveSettingsStep.Animate);

		const
			requestUid = fakeMd5(),
			properties = [];

		_.each(this.viewProperties(), (oItem) => {
			if (oItem.type() && oItem.type() !== ContactPropertyType.FullName && '' !== trim(oItem.value()))
			{
				properties.push([oItem.type(), oItem.value(), oItem.typeStr()]);
			}
		});

		Remote.contactSave((sResult, oData) => {

			let res = false;
			this.viewSaving(false);

			if (StorageResultType.Success === sResult && oData && oData.Result &&
				oData.Result.RequestUid === requestUid && 0 < pInt(oData.Result.ResultID))
			{
				if ('' === this.viewID())
				{
					this.viewID(pInt(oData.Result.ResultID));
				}

				this.reloadContactList();
				res = true;
			}

			_.delay(() => {
				this.viewSaveTrigger(res ? SaveSettingsStep.TrueResult : SaveSettingsStep.FalseResult);
			}, Magics.Time350ms);

			if (res)
			{
				this.watchDirty(false);

				_.delay(() => {
					this.viewSaveTrigger(SaveSettingsStep.Idle);
				}, Magics.Time1s);
			}

		}, requestUid, this.viewID(), properties);
	}

	@command((self) => !self.contacts.syncing() && !self.contacts.importing())
	syncCommand() {
		getApp().contactsSync((result, data) => {
			if (StorageResultType.Success !== result || !data || !data.Result)
			{
				window.alert(getNotification(
					data && data.ErrorCode ? data.ErrorCode : Notification.ContactsSyncError));
			}

			this.reloadContactList(true);
		});
	}

	getPropertyPlaceholder(type) {
		let result = '';
		switch (type)
		{
			case ContactPropertyType.LastName:
				result = 'CONTACTS/PLACEHOLDER_ENTER_LAST_NAME';
				break;
			case ContactPropertyType.FirstName:
				result = 'CONTACTS/PLACEHOLDER_ENTER_FIRST_NAME';
				break;
			case ContactPropertyType.Nick:
				result = 'CONTACTS/PLACEHOLDER_ENTER_NICK_NAME';
				break;
			// no default
		}

		return result;
	}

	addNewProperty(type, typeStr) {
		this.viewProperties.push(new ContactPropertyModel(type, typeStr || '', '', true, this.getPropertyPlaceholder(type)));
	}

	addNewOrFocusProperty(type, typeStr) {
		const item = _.find(this.viewProperties(), (prop) => type === prop.type());
		if (item)
		{
			item.focused(true);
		}
		else
		{
			this.addNewProperty(type, typeStr);
		}
	}

	addNewEmail() {
		this.addNewProperty(ContactPropertyType.Email, 'Home');
	}

	addNewPhone() {
		this.addNewProperty(ContactPropertyType.Phone, 'Mobile');
	}

	addNewWeb() {
		this.addNewProperty(ContactPropertyType.Web);
	}

	addNewNickname() {
		this.addNewOrFocusProperty(ContactPropertyType.Nick);
	}

	addNewNotes() {
		this.addNewOrFocusProperty(ContactPropertyType.Note);
	}

	addNewBirthday() {
		this.addNewOrFocusProperty(ContactPropertyType.Birthday);
	}

	exportVcf() {
		getApp().download(exportContactsVcf());
	}

	exportCsv() {
		getApp().download(exportContactsCsv());
	}

	initUploader() {
		if (this.importUploaderButton())
		{
			const
				j = new Jua({
					'action': uploadContacts(),
					'name': 'uploader',
					'queueSize': 1,
					'multipleSizeLimit': 1,
					'disableDragAndDrop': true,
					'disableMultiple': true,
					'disableDocumentDropPrevent': true,
					'clickElement': this.importUploaderButton()
				});

			if (j)
			{
				j
					.on('onStart', () => {
						this.contacts.importing(true);
					})
					.on('onComplete', (id, result, data) => {
						this.contacts.importing(false);
						this.reloadContactList();
						if (!id || !result || !data || !data.Result)
						{
							window.alert(i18n('CONTACTS/ERROR_IMPORT_FILE'));
						}
					});
			}
		}
	}

	removeCheckedOrSelectedContactsFromList() {
		const
			koContacts = this.contacts,
			contacts = this.contactsCheckedOrSelected();

		let
			currentContact = this.currentContact(),
			count = this.contacts().length;

		if (0 < contacts.length)
		{
			_.each(contacts, (contact) => {

				if (currentContact && currentContact.idContact === contact.idContact)
				{
					currentContact = null;
					this.currentContact(null);
				}

				contact.deleted(true);
				count -= 1;
			});

			if (0 >= count)
			{
				this.bDropPageAfterDelete = true;
			}

			_.delay(() => {
				_.each(contacts, (contact) => {
					koContacts.remove(contact);
					delegateRunOnDestroy(contact);
				});
			}, Magics.Time500ms);
		}
	}

	deleteSelectedContacts() {
		if (0 < this.contactsCheckedOrSelected().length)
		{
			Remote.contactsDelete(
				_.bind(this.deleteResponse, this),
				this.contactsCheckedOrSelectedUids()
			);

			this.removeCheckedOrSelectedContactsFromList();
		}
	}

	/**
	 * @param {string} sResult
	 * @param {AjaxJsonDefaultResponse} oData
	 */
	deleteResponse(sResult, oData) {
		if (Magics.Time500ms < (StorageResultType.Success === sResult && oData && oData.Time ? pInt(oData.Time) : 0))
		{
			this.reloadContactList(this.bDropPageAfterDelete);
		}
		else
		{
			_.delay(() => {
				this.reloadContactList(this.bDropPageAfterDelete);
			}, Magics.Time500ms);
		}
	}

	removeProperty(oProp) {
		this.viewProperties.remove(oProp);
		delegateRunOnDestroy(oProp);
	}

	/**
	 * @param {?ContactModel} contact
	 */
	populateViewContact(contact) {
		let
			id = '',
			lastName = '',
			firstName = '';
		const
			list = [];

		this.watchHash(false);

		this.emptySelection(false);
		this.viewReadOnly(false);

		if (contact)
		{
			id = contact.idContact;
			if (isNonEmptyArray(contact.properties))
			{
				_.each(contact.properties, (property) => {
					if (property && property[0])
					{
						if (ContactPropertyType.LastName === property[0])
						{
							lastName = property[1];
						}
						else if (ContactPropertyType.FirstName === property[0])
						{
							firstName = property[1];
						}
						else
						{
							list.push(new ContactPropertyModel(property[0], property[2] || '', property[1]));
						}
					}
				});
			}

			this.viewReadOnly(!!contact.readOnly);
		}

		list.unshift(new ContactPropertyModel(ContactPropertyType.LastName, '', lastName, false,
			this.getPropertyPlaceholder(ContactPropertyType.LastName)));

		list.unshift(new ContactPropertyModel(ContactPropertyType.FirstName, '', firstName, !contact,
			this.getPropertyPlaceholder(ContactPropertyType.FirstName)));

		this.viewID(id);

		delegateRunOnDestroy(this.viewProperties());

		this.viewProperties([]);
		this.viewProperties(list);

		this.watchDirty(false);
		this.watchHash(true);
	}

	/**
	 * @param {boolean=} dropPagePosition = false
	 */
	reloadContactList(dropPagePosition = false) {

		let offset = (this.contactsPage() - 1) * CONTACTS_PER_PAGE;

		this.bDropPageAfterDelete = false;

		if (dropPagePosition)
		{
			this.contactsPage(1);
			offset = 0;
		}

		this.contacts.loading(true);
		Remote.contacts((result, data) => {

			let
				count = 0,
				list = [];

			if (StorageResultType.Success === result && data && data.Result && data.Result.List)
			{
				if (isNonEmptyArray(data.Result.List))
				{
					list = _.map(data.Result.List, (item) => {
						const contact = new ContactModel();
						return contact.parse(item) ? contact : null;
					});

					list = _.compact(list);

					count = pInt(data.Result.Count);
					count = 0 < count ? count : 0;
				}
			}

			this.contactsCount(count);

			delegateRunOnDestroy(this.contacts());
			this.contacts(list);

			this.contacts.loading(false);
			this.viewClearSearch('' !== this.search());

		}, offset, CONTACTS_PER_PAGE, this.search());
	}

	onBuild(dom) {
		this.oContentVisible = $('.b-list-content', dom);
		this.oContentScrollable = $('.content', this.oContentVisible);

		this.selector.init(this.oContentVisible, this.oContentScrollable, KeyState.ContactList);

		key('delete', KeyState.ContactList, () => {
			this.deleteCommand();
			return false;
		});

		key('c, w', KeyState.ContactList, () => {
			this.newMessageCommand();
			return false;
		});

		const self = this;

		dom
			.on('click', '.e-pagenator .e-page', function() { // eslint-disable-line prefer-arrow-callback
				const page = ko.dataFor(this); // eslint-disable-line no-invalid-this
				if (page)
				{
					self.contactsPage(pInt(page.value));
					self.reloadContactList();
				}
			});

		this.initUploader();
	}

	onShow(bBackToCompose, sLastComposeFocusedField) {
		this.bBackToCompose = isUnd(bBackToCompose) ? false : !!bBackToCompose;
		this.sLastComposeFocusedField = isUnd(sLastComposeFocusedField) ? '' : sLastComposeFocusedField;

		routeOff();
		this.reloadContactList(true);
	}

	onHide() {
		routeOn();

		this.currentContact(null);
		this.emptySelection(true);
		this.search('');
		this.contactsCount(0);

		delegateRunOnDestroy(this.contacts());
		this.contacts([]);

		this.sLastComposeFocusedField = '';

		if (this.bBackToCompose)
		{
			this.bBackToCompose = false;

			if (Settings.capa(Capa.Composer))
			{
				showScreenPopup(require('View/Popup/Compose'));
			}
		}
	}
}

export {ContactsPopupView, ContactsPopupView as default};
