<!--
  - @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
  -
  - @author John Molakvoæ <skjnldsv@protonmail.com>
  - @author Charismatic Claire <charismatic.claire@noservice.noreply>
  -
  - @license GNU AGPL version 3 or any later version
  -
  - This program is free software: you can redistribute it and/or modify
  - it under the terms of the GNU Affero General Public License as
  - published by the Free Software Foundation, either version 3 of the
  - License, or (at your option) any later version.
  -
  - This program is distributed in the hope that it will be useful,
  - but WITHOUT ANY WARRANTY; without even the implied warranty of
  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  - GNU Affero General Public License for more details.
  -
  - You should have received a copy of the GNU Affero General Public License
  - along with this program. If not, see <http://www.gnu.org/licenses/>.
  -
  -->

<template>
	<Content app-name="contacts" :class="{'icon-loading': loading}">
		<!-- new-contact-button + navigation + settings -->
		<AppNavigation>
			<!-- new-contact-button -->
			<AppNavigationNew v-if="!loading" button-id="new-contact-button" :text="t('contacts','New contact')"
				button-class="icon-add" :disabled="!defaultAddressbook" @click="newContact" />

			<!-- groups list -->
			<ul v-if="!loading" id="groups-list">
				<AppNavigationItem v-for="item in menu" :key="item.key" :item="item" />
			</ul>

			<!-- settings -->
			<AppNavigationSettings v-if="!loading">
				<SettingsSection />
			</AppNavigationSettings>
		</AppNavigation>

		<AppContent>
			<!-- go back to list when in details mode -->
			<div v-if="selectedContact && isMobile" id="app-details-toggle" class="icon-confirm"
				tabindex="0" @click="showList" />

			<div id="app-content-wrapper">
				<!-- contacts list -->
				<ContactsList :list="contactsList" :contacts="contacts" :loading="loading"
					:search-query="searchQuery" />

				<!-- main contacts details -->
				<ContactDetails :loading="loading" :contact-key="selectedContact" />
			</div>
		</AppContent>
		<Modal v-if="isImporting" :clear-view-delay="-1" :can-close="isImportDone"
			@close="closeImport">
			<ImportScreen />
		</Modal>
	</Content>
</template>

<script>
import {
	AppContent,
	AppNavigation,
	AppNavigationItem,
	AppNavigationNew,
	AppNavigationSettings,
	Content,
	Modal
} from 'nextcloud-vue'
import isMobile from 'nextcloud-vue/dist/Mixins/isMobile'

import moment from 'moment'
import download from 'downloadjs'
import { VCardTime } from 'ical.js'

import SettingsSection from 'Components/SettingsSection'
import ContactsList from 'Components/ContactsList'
import ContactDetails from 'Components/ContactDetails'
import ImportScreen from 'Components/ImportScreen'

import Contact from 'Models/contact'
import rfcProps from 'Models/rfcProps'

import client from 'Services/cdav'

const GROUP_ALL_CONTACTS = t('contacts', 'All contacts')
const GROUP_NO_GROUP_CONTACTS = t('contacts', 'Not grouped')

export default {
	name: 'Contacts',

	components: {
		AppContent,
		AppNavigation,
		AppNavigationItem,
		AppNavigationNew,
		AppNavigationSettings,
		ContactDetails,
		ContactsList,
		Content,
		ImportScreen,
		Modal,
		SettingsSection
	},

	mixins: [
		isMobile
	],

	// passed by the router
	props: {
		selectedGroup: {
			type: String,
			default: undefined,
			required: true
		},
		selectedContact: {
			type: String,
			default: undefined
		}
	},

	data() {
		return {
			loading: true,
			searchQuery: ''
		}
	},

	computed: {
		// store getters
		addressbooks() {
			return this.$store.getters.getAddressbooks
		},
		sortedContacts() {
			return this.$store.getters.getSortedContacts
		},
		contacts() {
			return this.$store.getters.getContacts
		},
		groups() {
			return this.$store.getters.getGroups
		},
		orderKey() {
			return this.$store.getters.getOrderKey
		},
		importState() {
			return this.$store.getters.getImportState
		},

		// importing states
		isImporting() {
			return this.importState.stage !== 'default'
		},
		isImportDone() {
			return this.importState.stage === 'done'
		},

		// first enabled addressbook of the list
		defaultAddressbook() {
			return this.addressbooks.find(addressbook => !addressbook.readOnly && addressbook.enabled)
		},

		/**
		 * Contacts list based on the selected group.
		 * Those filters are pretty fast, so let's only
		 * intersect the groups contacts and the full
		 * sorted contacts List.
		 *
		 * @returns {Array}
		 */
		contactsList() {
			if (this.selectedGroup === GROUP_ALL_CONTACTS) {
				return this.sortedContacts
			} else if (this.selectedGroup === GROUP_NO_GROUP_CONTACTS) {
				return this.ungroupedContacts.map(contact => this.sortedContacts.find(item => item.key === contact.key))
			}
			let group = this.groups.filter(group => group.name === this.selectedGroup)[0]
			if (group) {
				return this.sortedContacts.filter(contact => group.contacts.indexOf(contact.key) >= 0)
			}
			return []
		},

		ungroupedContacts() {
			return this.sortedContacts.filter(contact => this.contacts[contact.key].groups && this.contacts[contact.key].groups.length === 0)
		},

		// generate groups menu from groups store
		groupsMenu() {
			return this.groups.map(group => {
				return {
					id: group.name.replace(' ', '_'),
					key: group.name.replace(' ', '_'),
					router: {
						name: 'group',
						params: { selectedGroup: group.name }
					},
					text: group.name,
					utils: {
						counter: group.contacts.length,
						actions: [
							{
								icon: 'icon-download',
								text: 'Download',
								action: () => this.downloadGroup(group)
							}
						]
					}
				}
			}).sort(function(a, b) {
				return parseInt(b.utils.counter) - parseInt(a.utils.counter)
			})
		},

		// building the main menu
		menu() {
			return this.groupAllGroup.concat(this.groupNotGrouped.concat(this.groupsMenu))
		},

		// default group for every contacts
		groupAllGroup() {
			return [{
				id: 'everyone',
				key: 'everyone',
				icon: 'icon-contacts-dark',
				router: {
					name: 'group',
					params: { selectedGroup: GROUP_ALL_CONTACTS }
				},
				text: GROUP_ALL_CONTACTS,
				utils: {
					counter: this.sortedContacts.length
				}
			}]
		},

		// default group for every contacts
		groupNotGrouped() {
			if (this.ungroupedContacts.length === 0) {
				return []
			}
			return [{
				id: 'notgrouped',
				key: 'notgrouped',
				icon: 'icon-user',
				router: {
					name: 'group',
					params: { selectedGroup: GROUP_NO_GROUP_CONTACTS }
				},
				text: GROUP_NO_GROUP_CONTACTS,
				utils: {
					counter: this.ungroupedContacts.length
				}
			}]
		}
	},

	watch: {
		// watch url change and group select
		selectedGroup: function() {
			if (!this.isMobile) {
				this.selectFirstContactIfNone()
			}
		},
		// watch url change and contact select
		selectedContact: function() {
			if (!this.isMobile) {
				this.selectFirstContactIfNone()
			}
		}
	},

	mounted() {
		/**
		 * Register search
		 */
		this.search = new OCA.Search(this.search, this.resetSearch)
	},

	beforeMount() {
		// get addressbooks then get contacts
		client.connect({ enableCardDAV: true }).then(() => {
			console.debug('Connected to dav!', client)
			this.$store.dispatch('getAddressbooks')
				.then((addressbooks) => {

					// No addressbooks? Create a new one!
					if (addressbooks.length === 0) {
						this.$store.dispatch('appendAddressbook', { displayName: t('contacts', 'Contacts') })
							.then(() => {
								this.fetchContacts()
							})
					// else, let's get those contacts!
					} else {
						this.fetchContacts()
					}
				})
				// check local storage for orderKey
			if (localStorage.getItem('orderKey')) {
				// run setOrder mutation with local storage key
				this.$store.commit('setOrder', localStorage.getItem('orderKey'))
			}
		})
	},

	methods: {
		async newContact() {
			const rev = new VCardTime()
			const contact = new Contact(`
				BEGIN:VCARD
				VERSION:4.0
				PRODID:-//Nextcloud Contacts v${appVersion}
				END:VCARD
			`.trim().replace(/\t/gm, ''),
			this.defaultAddressbook)

			contact.fullName = t('contacts', 'New contact')
			rev.fromUnixTime(Date.now() / 1000)
			contact.rev = rev

			// itterate over all properties (filter is not usable on objects and we need the key of the property)
			const properties = rfcProps.properties(this)
			for (let name in properties) {
				if (properties[name].default) {
					let defaultData = properties[name].defaultValue
					// add default field
					let property = contact.vCard.addPropertyWithValue(name, defaultData.value)
					// add default type
					if (defaultData.type) {
						property.setParameter('type', defaultData.type)

					}
				}
			}

			// set group if it's selected already
			// BUT NOT if it's the _fake_ groups like all contacts and not grouped
			if ([GROUP_ALL_CONTACTS, GROUP_NO_GROUP_CONTACTS].indexOf(this.selectedGroup) === -1) {
				contact.groups = [ this.selectedGroup ]
			}
			try {
				// this will trigger the proper commits to groups, contacts and addressbook
				await this.$store.dispatch('addContact', contact)
				await this.$router.push({
					name: 'contact',
					params: {
						selectedGroup: this.selectedGroup,
						selectedContact: contact.key
					}
				})
			} catch (error) {
				OC.Notification.showTemporary(t('contacts', 'Unable to create the contact.'))
				console.error(error)
			}
		},

		/**
		 * Dispatch sorting update request to the store
		 *
		 * @param {string} orderKey the object key to order by
		 */
		updateSorting(orderKey = 'displayName') {
			this.$store.commit('setOrder', orderKey)
			this.$store.commit('sortContacts')
		},

		/**
		 * Fetch the contacts of each addressbooks
		 */
		fetchContacts() {
			// wait for all addressbooks to have fetch their contacts
			Promise.all(this.addressbooks.map(addressbook => {
				if (addressbook.enabled) {
					return this.$store.dispatch('getContactsFromAddressBook', { addressbook })
				}
			})).then(results => {
				this.loading = false
				if (!this.isMobile) {
					this.selectFirstContactIfNone()
				}
			})
		},

		/**
		 * Select the first contact of the list
		 * if none are selected already
		 */
		selectFirstContactIfNone() {
			let inList = this.contactsList.findIndex(contact => contact.key === this.selectedContact) > -1
			if (this.selectedContact === undefined || !inList) {
				if (this.selectedContact && !inList) {
					OC.Notification.showTemporary(t('contacts', 'Contact not found'))
				}
				if (Object.keys(this.contactsList).length) {
					this.$router.push({
						name: 'contact',
						params: {
							selectedGroup: this.selectedGroup,
							selectedContact: Object.values(this.contactsList)[0].key
						}
					})
				}
			}
		},

		/**
		 * Download vcard promise as vcard file
		 *
		 * @param {Promise} vcardPromise the full vcf file promise
		 */
		async downloadVcardPromise(vcardPromise) {
			vcardPromise.then(response => {
				const filename = moment().format('YYYY-MM-DD_HH-mm') + '_' + response.groupName + '.vcf'
				download(response.data, filename, 'text/vcard')
			})
		},

		/**
		 * Download group of contacts
		 *
		 * @param {Object} group of contacts to be downloaded
		 */
		downloadGroup(group) {
			// get grouped contacts
			let groupedContacts = {}
			group.contacts.map((key) => {
				const id = this.contacts[key].addressbook.id
				groupedContacts = Object.assign({
					[id]: {
						addressbook: this.contacts[key].addressbook,
						contacts: []
					}
				}, groupedContacts)
				groupedContacts[id].contacts.push(this.contacts[key].url)
			})
			// create vcard promise with the requested contacts
			const vcardPromise = Promise.all(
				Object.keys(groupedContacts).map(key =>
					groupedContacts[key].addressbook.dav.addressbookMultigetExport(groupedContacts[key].contacts)))
				.then(response => ({
					groupName: group.name,
					data: response.map(data => data.body).join('')
				}))
			// download vcard
			this.downloadVcardPromise(vcardPromise)
		},

		/* SEARCH */
		search(query) {
			this.searchQuery = query
		},
		resetSearch() {
			this.searchQuery = ''
		},

		/**
		 * Show the list and deselect contact
		 */
		showList() {
			// Reset the selected contact
			this.$router.push({
				name: 'contact',
				params: {
					selectedGroup: this.selectedGroup,
					selectedContact: undefined
				}
			})
		},

		/**
		 * Done importing, the user closed the import status screen
		 */
		closeImport() {
			// TODO: remove after https://github.com/nextcloud/nextcloud-vue/pull/323
			if (this.isImportDone) {
				this.$store.dispatch('changeStage', 'default')
			}
		}
	}
}
</script>
