import orderBy from 'lodash/orderBy';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';

import { compareServerVersion } from '../utils';
import { store as reduxStore } from '../auxStore';
import database from '../database';
import log from '../../utils/log';
import { setCustomEmojis as setCustomEmojisAction } from '../../actions/customEmojis';

const getUpdatedSince = allEmojis => {
	if (!allEmojis.length) {
		return null;
	}
	const ordered = orderBy(
		allEmojis.filter(item => item._updatedAt !== null),
		['_updatedAt'],
		['desc']
	);
	return ordered && ordered[0]._updatedAt.toISOString();
};

const updateEmojis = async ({ update = [], remove = [], allRecords }) => {
	if (!((update && update.length) || (remove && remove.length))) {
		return;
	}
	const db = database.active;
	const emojisCollection = db.get('custom_emojis');
	let emojisToCreate = [];
	let emojisToUpdate = [];
	let emojisToDelete = [];

	// Create or update
	if (update && update.length) {
		emojisToCreate = update.filter(i1 => !allRecords.find(i2 => i1._id === i2.id));
		emojisToUpdate = allRecords.filter(i1 => update.find(i2 => i1.id === i2._id));
		emojisToCreate = emojisToCreate.map(emoji =>
			emojisCollection.prepareCreate(e => {
				e._raw = sanitizedRaw({ id: emoji._id }, emojisCollection.schema);
				Object.assign(e, emoji);
			})
		);
		emojisToUpdate = emojisToUpdate.map(emoji => {
			const newEmoji = update.find(e => e._id === emoji.id);
			return emoji.prepareUpdate(e => {
				Object.assign(e, newEmoji);
			});
		});
	}

	if (remove && remove.length) {
		emojisToDelete = allRecords.filter(i1 => remove.find(i2 => i1.id === i2._id));
		emojisToDelete = emojisToDelete.map(emoji => emoji.prepareDestroyPermanently());
	}

	try {
		await db.action(async () => {
			await db.batch(...emojisToCreate, ...emojisToUpdate, ...emojisToDelete);
		});
		return true;
	} catch (e) {
		log(e);
	}
};

export async function setCustomEmojis() {
	const db = database.active;
	const emojisCollection = db.get('custom_emojis');
	const allEmojis = await emojisCollection.query().fetch();
	const parsed = allEmojis.reduce((ret, item) => {
		ret[item.name] = {
			name: item.name,
			extension: item.extension
		};
		item.aliases.forEach(alias => {
			ret[alias] = {
				name: item.name,
				extension: item.extension
			};
		});
		return ret;
	}, {});
	reduxStore.dispatch(setCustomEmojisAction(parsed));
}

export function getCustomEmojis() {
	return new Promise(async resolve => {
		try {
			const serverVersion = reduxStore.getState().server.version;
			const db = database.active;
			const emojisCollection = db.get('custom_emojis');
			const allRecords = await emojisCollection.query().fetch();
			const updatedSince = await getUpdatedSince(allRecords);

			// if server version is lower than 0.75.0, fetches from old api
			if (compareServerVersion(serverVersion, 'lowerThan', '0.75.0')) {
				// RC 0.61.0
				const result = await this.sdk.get('emoji-custom');

				let { emojis } = result;
				emojis = emojis.filter(emoji => !updatedSince || emoji._updatedAt > updatedSince);
				const changedEmojis = await updateEmojis({ update: emojis, allRecords });

				// `setCustomEmojis` is fired on selectServer
				// We run it again only if emojis were changed
				if (changedEmojis) {
					setCustomEmojis();
				}
				return resolve();
			} else {
				const params = {};
				if (updatedSince) {
					params.updatedSince = updatedSince;
				}

				// RC 0.75.0
				const result = await this.sdk.get('emoji-custom.list', params);

				if (!result.success) {
					return resolve();
				}

				const { emojis } = result;
				const { update, remove } = emojis;
				const changedEmojis = await updateEmojis({ update, remove, allRecords });

				// `setCustomEmojis` is fired on selectServer
				// We run it again only if emojis were changed
				if (changedEmojis) {
					setCustomEmojis();
				}
			}
		} catch (e) {
			log(e);
			return resolve();
		}
	});
}