'use strict';
const coreEvents = require('./coreEvents');
const coreUtils = require('./coreUtils');
const coreWebsocket = require('./coreWebsocket');
const globalObject = require('./globalObject');

const assetsModule = {};

// Create internal event that other modules may subscribe to
coreEvents.createEvent('asset');

// Create an internal event to wait for tags array
coreEvents.createEvent('receivedAssets');
let weHaveReceivedTags = false;

// Create event in userland.
globalObject.createEvent('asset');

let assets;

const websocket = coreWebsocket.copy((event) => event.data.startsWith('{"wa":'));
const webstrateId = coreUtils.getLocationObject().webstrateId;

function waitForAssets() {
	return new Promise((resolve, reject)=>{
		if (weHaveReceivedTags) {
			resolve();
		} else {
			coreEvents.addEventListener('receivedAssets', function once() {
				coreEvents.removeEventListener('receivedAssets', once);
				weHaveReceivedTags = true;
				resolve();
			});
		}
	});
}

websocket.onjsonmessage = (message) => {
	if (message.d !== webstrateId) return;
	switch (message.wa) {
		case 'assets':
			assets = message.assets;
			coreEvents.triggerEvent('receivedAssets', assets);
			weHaveReceivedTags = true;
			break;
		case 'asset':
			waitForAssets().then(()=>{
				assets.push(message.asset);
				coreEvents.triggerEvent('asset', message.asset);
				globalObject.triggerEvent('asset', message.asset);
			});
			break;
	}
};

// Define webstrate.assets. Returns a frozen copy, so users won't modify it.
Object.defineProperty(globalObject.publicObject, 'assets', {
	get: () => {
		return Object.freeze(coreUtils.objectClone(assets));
	}
});

/**
 * Makes it possible to select and upload files.
 * @param  {Function} callback Callback with two arguments, error and response. First argument will
 *                             be null on success.
 * @return {Promise}  Promise that gets resolved with the result.
 * @public
 */
globalObject.publicObject.uploadAsset = (callback = () => {}, options = {}) => {
	return new Promise((accept, reject) => {
		const input = document.createElement('input');
		input.setAttribute('multiple', true);
		input.setAttribute('type', 'file');

		input.addEventListener('change', event => {
			const formData = new FormData();
			Object.entries(options).forEach(([key, value]) => formData.append(key, value));

			for (let i=0; i < input.files.length; i++) {
				formData.append('file[]', input.files.item(i));
			}

			fetch('', {
				method: 'post',
				credentials: 'include',
				body: formData
			})
				.then(res => res.json()
					.then(json => {
						if (res.ok) {
							accept(json);
							callback(null, json);
						} else {
							reject(json.error);
							callback(json.error);
						}
					})
					.catch(err => {
						reject(err);
						callback(err);
					})
				)
				.catch(err => {
					reject(err);
					callback(err);
				});
		});

		input.click();
	});
};

globalObject.publicObject.deleteAsset = (assetName, callback) => {
	websocket.send({ wa: 'deleteAsset', d: webstrateId, assetName },
		(err, result) => callback && callback(err, result));
};

globalObject.publicObject.searchAsset = (assetIdentifier, query = {}, callback) => {
	if (typeof callback !== 'function') throw new Error('Must provide callback function');

	const [assetName, assetVersion] = assetIdentifier.split('/');
	websocket.send({ wa: 'assetSearch', d: webstrateId, assetName, assetVersion: +assetVersion,
		query: query.query, sort: query.sort, limit: query.limit, skip: query.skip },
	(err, result) => callback(err, result.records, result.count));
};

module.exports = assetsModule;