OIPApis

import axios from 'axios';
import MPSingle from './OIPComponents/MPSingle'
import FloDataTX from './OIPComponents/FloDataTX'
import Artifact from './Artifacts/Artifact'

/**
 * The Transaction ID on the Blockchain.
 * @typedef {string} TXID
 * @example <caption>Full TXID Reference</caption>
 * 8a83ecb7812ca2770814d996529e153b07b103424cd389b800b743baa9604c5b
 * @example <caption>Shortened TXID Reference</caption>
 * 8a83ec
 */

const hydrateArray = (artifacts) => {
	let tmpArray = []
	for (let art of artifacts) {
		tmpArray.push(Artifact(art))
	}
	return tmpArray
}

const hydrateFloDataTX = (floDataTXs) => {
	let tmpArray = []
	for (let data of floDataTXs) {
		tmpArray.push(new FloDataTX(data))
	}
	return tmpArray
}
	//ToDo: change to 'https' when ready
const localhost = "http://localhost:1606"
const defaultOIPdURL = "http://snowflake.oip.fun:1606";

class OIPAPI {
	/**
	 * Spawn a new OIP Index with a specific OIPd URL
	 * @param  {Object} [settings] - The Settings to use for the Index
	 * @param {string} [settings.OIPdURL="https://snowflake.oip.fun/alexandria/v2"] [description]
	 * @return {Index}
	 */
	constructor(settings) {
		if (settings && settings.OIPdURL) {
			this.setOIPdURL(settings.OIPdURL)
			//ToDo: switch back to defaultOIPdURL
		} else this.setOIPdURL(defaultOIPdURL)
	}

	setOIPdURL(OIPdURL) {
		this.url = OIPdURL;

		this.index = new axios.create({
			baseURL: this.url,
			headers: {
				'Access-Control-Allow-Origin': '*',
			},
		})
	}

	getOIPdURL() {
		return this.url
	}

	getNetwork() {
		return this.index
	}

	/**
	 * Search The Index for Artifacts that match your search query
	 * @param {string} query - your search query
	 * @param {number} [limit=100] - max num of results
	 * @return {Promise<Object>}
	 * //return example
	 * {success: true, artifacts: artifacts, ...}
	 *
	 * //or error
	 * {success: false, error: error, ...}
	 */
	async searchArtifacts(query, limit) {
		if (typeof query !== 'string') {
			return {success: false, error: `'query' must be of type string`}
		}
		let res;
		try {
			res = await this.index.get(`/artifact/search`, {
				params: {
					q: query
				},
				limit
			})
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let artifacts = res.data.results
			if (artifacts.length === 0)
				return {success: true, message: `No artifacts found with query: ${query}`, artifacts}
			return {success: true, artifacts: hydrateArray(artifacts)}
		} else {
			return {success: false, error: 'No data returned from axios request', response: res.data}
		}
	}

	/**
	 * Get an Artifact from the Index by TXID
	 * @param {string} txid  - transaction id of the artifact you wish to retrieve
	 * @return {Promise<Object>} Returns a Promise that will resolve to an Artifact or an object containing an error
	 * @example
	 * //return example
	 * {success: true, artifact: Artifact}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async getArtifact(txid) {
		let res
		try {
			res = await this.index.get(`/artifact/get/${txid}`);
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let resultArray = res.data.results

			if (resultArray.length === 0) {
				return {success: false, error: "No results found", response: res.data}
			} else if (resultArray.length > 1) {
				return {success: false, error: "Multiple artifacts found, possible collision", artifacts: resultArray}
			} else return {success: true, artifact: Artifact(resultArray[0])}

		} else {
			return {success: false, error: 'No data returned from axios request', response: res.data}
		}
	}

	/**
	 * Get multiple Artifacts by their TXID
	 * @param {Array.<string>} txids - an array of transaction IDs
	 * @return {Promise<Object>}
	 * @example
	 * //return example
	 * {success: true, artifacts: artifacts>}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async getArtifacts(txids) {
		if (!Array.isArray(txids)) {
			return {success: false, error: `'txids' must be an Array of transaction IDs`}
		}
		let artifacts = []
		let errors = []
		for (let txid of txids) {
			let res
			try {
				res = await this.getArtifact(txid)
			} catch (err) {
				return {success: false, error: err}
			}
			if (res.success) artifacts.push(res.artifact)
			else errors.push(res.artifact)
		}
		if (errors.length > 0) {
			return {success: false, error: 'Not [all artifacts] found', errors, artifacts}
		} else {
			return {success: true, artifacts: artifacts}
		}
	}

	/**
	 * Get the Latest Artifacts published to the Index
	 * @param {number} [limit=100] - The amount of artifacts you want returns ( max: 1000 )
	 * @param {boolean} [nsfw=false] - not safe for work artifacts (don't be sick!)
	 * @return {Promise<Object>}
	 * @example
	 * //return example
	 * {success: true, artifacts: Array.<Artifact>}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async getLatestArtifacts(limit = 100, nsfw = false) {
		let res
		try {
			res = await this.index.get(`/artifact/get/latest/${limit}`, {
				params: {
					nsfw
				}
			});
		} catch (err) {
			return {success: false, error: err}
		}

		if (res && res.data) {
			let artifacts = res.data.results
			if (artifacts.length === 0)
				return {success: false, error: 'No artifacts found', response: res.data}
			if (artifacts.length > 0 && artifacts.length < limit)
				return {success: true, artifacts: hydrateArray(artifacts), message: 'Not all requested artifacts were found'}
			if (artifacts.length === limit)
				return {success: true, artifacts: hydrateArray(artifacts)}
		} else {
			return {success: false, error: 'No data returned from axios request', response: res.data}
		}
	}

	/**
	 * Get the Latest OIP041 Artifacts published to the Index
	 * @param {number} [limit=100] - The amount of artifacts you want returns ( max: 1000 )
	 * @param {boolean} [nsfw=false] - not safe for work artifacts (don't be sick!)
	 * @return {Promise<Object>}
	 * @example
	 * //return example
	 * {success: true, artifacts: Array.<Artifact>}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async getLatest041Artifacts(limit = 100, nsfw = false) {
		let res
		try {
			res = await this.index.get(`/oip041/artifact/get/latest/${limit}`, {
				params: {
					nsfw
				}
			});
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let artifacts = res.data.results
			if (artifacts.length === 0)
				return {success: false, error: 'No artifacts found', response: res.data}
			if (artifacts.length > 0 && artifacts.length < limit)
				return {success: true, artifacts: hydrateArray(artifacts), warning: 'Not all requested artifacts were found'}
			if (artifacts.length === limit) {
				return {success: true, artifacts: hydrateArray(artifacts)}
			}

		} else {
			return {success: false, error: 'No data returned from axios request', response: res}
		}
	}

	/**
	 * Get an OIP041 Artifact from the Index by TXID
	 * @param {string} txid  - transaction id of the artifact you wish to retrieve
	 * @return {Promise<Object>} Returns a Promise that will resolve to an Artifact or an object containing an error
	 * @example
	 * //return example
	 * {success: true, artifact: Artifact}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async get041Artifact(txid) {
		let res
		try {
			res = await this.index.get(`/oip041/artifact/get/${txid}`);
		} catch (err) {
			return {success: false, error: err}
		}

		if (res && res.data) {
			let resultArray = res.data.results

			if (resultArray.length === 0) {
				return {success: false, error: "No results found", response: res.data}
			} else if (resultArray.length > 1) {
				return {success: false, error: "Multiple artifacts found, possible collision", artifacts: resultArray}
			} else return {success: true, artifact: Artifact(resultArray[0])}

		} else {
			return {success: false, error: 'No data returned from axios request', response: res.data}
		}
	}

	/**
	 * Get multiple OIP041 Artifacts by their TXID
	 * @param {Array.<string>} txids - an array of transaction IDs
	 * @return {Promise<Object>}
	 * @example
	 * //return example
	 * {success: true, artifacts: artifacts>}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async get041Artifacts(txids) {
		if (!Array.isArray(txids)) {
			return {success: false, error: `'txids' must be an Array of transaction IDs`}
		}
		let artifacts = []
		let errors = []
		for (let txid of txids) {
			let res
			try {
				res = await this.get041Artifact(txid)
			} catch (err) {
				return {success: false, error: err}
			}
			if (res.success) artifacts.push(res.artifact)
			else errors.push(res.artifact)
		}
		if (errors.length > 0) {
			return {success: false, error: 'Not [all artifacts] found', errors, artifacts}
		} else {
			return {success: true, artifacts: artifacts}
		}
	}

	/**
	 * Get the Latest OIP042 Artifacts published to the Index
	 * @param {number} [limit=100] - The amount of artifacts you want returns ( max: 1000 )
	 * @param {boolean} [nsfw=false] - not safe for work artifacts (don't be sick!)
	 * @return {Promise<Object>}
	 * @example
	 * //return example
	 * {success: true, artifacts: Array.<Artifact>}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async getLatest042Artifacts(limit = 100 , nsfw = false) {
		let res
		try {
			res = await this.index.get(`/oip042/artifact/get/latest/${limit}`, {
				params: {
					nsfw
				}
			});
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let artifacts = res.data.results
			if (artifacts.length === 0)
				return {success: false, error: 'No artifacts found', response: res.data}
			if (artifacts.length > 0 && artifacts.length < limit)
				return {success: true, artifacts: hydrateArray(artifacts), warning: 'Not all requested artifacts were found'}
			if (artifacts.length === limit) {
				return {success: true, artifacts: hydrateArray(artifacts)}
			}

		} else {
			return {success: false, error: 'No data returned from axios request', response: res}
		}
	}

	/**
	 * Get the Latest Alexandria Media Artifacts published to the Index
	 * @param {number} [limit=100] - The amount of artifacts you want returns ( max: 1000 )
	 * @param {boolean} [nsfw=false] - not safe for work artifacts (don't be sick!)
	 * @return {Promise<Object>}
	 * @example
	 * //return example
	 * {success: true, artifacts: Array.<Artifact>}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async getLatestAlexandriaMediaArtifacts(limit = 100, nsfw = false) {
		let res
		try {
			res = await this.index.get(`/alexandria/artifact/get/latest/${limit}`, {
				params: {
					nsfw
				}
			});
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let artifacts = res.data.results
			if (artifacts.length === 0)
				return {success: false, error: 'No artifacts found', response: res.data}
			if (artifacts.length > 0 && artifacts.length < limit)
				return {success: true, artifacts: hydrateArray(artifacts), warning: 'Not all requested artifacts were found'}
			if (artifacts.length === limit) {
				return {success: true, artifacts: hydrateArray(artifacts)}
			}

		} else {
			return {success: false, error: 'No data returned from axios request', response: res}
		}
	}

	/**
	 * Get an Alexandria Media Artifact from the Index by TXID
	 * @param {string} txid  - transaction id of the artifact you wish to retrieve
	 * @return {Promise<Object>} Returns a Promise that will resolve to an Artifact or an object containing an error
	 * @example
	 * //return example
	 * {success: true, artifact: Artifact}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async getAlexandriaMediaArtifact(txid) {
		let res
		try {
			res = await this.index.get(`/alexandria/artifact/get/${txid}`);
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let resultArray = res.data.results

			if (resultArray.length === 0) {
				return {success: false, error: "No results found", response: res.data}
			} else if (resultArray.length > 1) {
				return {success: false, error: "Multiple artifacts found, possible collision", artifacts: resultArray}
			} else return {success: true, artifact: Artifact(resultArray[0])}

		} else {
			return {success: false, error: 'No data returned from axios request', response: res.data}
		}
	}

	/**
	 * Get one or more Alexandria Media Artifacts by their TXID
	 * @param {Array.<string>} txids - an array of transaction IDs
	 * @return {Promise<Object>}
	 * @example
	 * //return example
	 * {success: true, artifacts: artifacts}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async getAlexandriaMediaArtifacts(txids) {
		if (!Array.isArray(txids)) {
			return {success: false, error: `'txids' must be an Array of transaction IDs`}
		}
		let artifacts = []
		let errors = []
		for (let txid of txids) {
			let res
			try {
				res = await this.getAlexandriaMediaArtifact(txid)
			} catch (err) {
				return {success: false, error: err}
			}
			if (res.success) artifacts.push(res.artifact)
			else errors.push(res.artifact)
		}
		if (errors.length > 0) {
			return {success: false, error: 'Not [all artifacts] found', errors, artifacts}
		} else {
			return {success: true, artifacts: artifacts}
		}
	}

	/**
	 * Search all the floData published into the Flo Blockchain, this is provided by a connection to an OIPd server
	 * @param {string} query - your search query
	 * @param {number} [limit] - max num of results
	 * @return {Promise<Object>}
	 * //return example
	 * {success: true, floData: Array.<FloDataTXs>}
	 *
	 * //or error
	 * {success: false, error: error}
	 */
	async searchFloData(query, limit) {
		if (typeof query !== 'string') {
			return {success: false, error: `'query' must be of type string`}
		}
		let res;
		try {
			res = await this.index.get(`/floData/search`, {
				params: {
					q: query
				},
				limit
			})
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let txs = res.data.results
			//ToDo: consider not initializing a class for tx data
			return {success: true, txs: hydrateFloDataTX(txs)}
		} else {
			return {success: false, error: 'Missing axios data response', response: res.data}
		}
	}

	/**
	 * Get floData by TXID
	 * @param {string} txid - the transaction id you wish to grab the floData from
	 */
	async getFloData(txid) {
		let res;
		try {
			res = await this.index.get(`/floData/get/${txid}`)
		} catch (err) {
			return {sucess: false, error: err}
		}
		if (res && res.data) {
			let results = res.data.results
			if (results.length === 0) {
				return {success: false, error: 'No floData found', response: res.data}
			} else if (results.length > 1) {
				return {success: false, error: 'Collision: multiple data points found', response: res.data}
			} else {
				return {success: true, floData: res.data.results[0].tx.floData}
			}
		} else {
			return {success: false, error: 'Missing data', response: res.data}
		}

	}

	/**
	 * Get a Multipart by its TXID
	 * @param txid
	 * @return {Promise<Object>}
	 */
	async getMultipart(txid) {
		let res;
		try {
			res = await this.index.get(`/multipart/get/id/${txid}`)
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let total = res.data.total
			let results = res.data.results
			let multiparts = []
			for (let mp of results) {
				multiparts.push(new MPSingle(mp))
			}
			if (!total) {
				return {success: false, message: "No parts found", responseData: res.data}
			} else if (total > 1) {
				return {success: false, message: "Collision: mulitple parts found with single ID", multiparts}
			} else {
				return {success: true, multipart: multiparts[0]}
			}
		} else {
			return {success: false, error: "Missing axios data response", response: res}
		}

	}

	/**
	 * Get OIP Multiparts by the First TXID Reference
	 * @param {string} ref - the TXID reference of the first multipart
	 * @param {number} [limit] - max num of results
	 * @return {Promise<Object>}
	 */
	async getMultiparts(ref, limit) {
		let res;
		let querystring = `/multipart/get/ref/${ref}`
		if (limit) querystring += `/${limit}`
		try {
			res = await this.index.get(querystring)
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let total = res.data.total
			let results = res.data.results
			let multiparts = []
			for (let mp of results) {
				multiparts.push(new MPSingle(mp))
			}
			return {success: true, multiparts, total}

		} else {
			return {success: false, error: "Missing axios data response", response: res}
		}

	}

	/**
	 * Get a historian data point by its txid
	 * @param {string} txid
	 * @return {Promise<Object>}
	 */
	async getHistorianData(txid) {
		let res
		try {
			res = await this.index.get(`historian/get/${txid}`)
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let result = res.data.results
			if (Array.isArray(result)) {
				if (result.length === 1) {
					result = result[0]
				}
				return {success: true, result}
			}
		} else {
			return {success: false, response: res.data, error: "No data returned from axios request"}
		}
	}

	/**
	 * Get the latest historian data points
	 * @param {number} [limit=100]
	 * @return {Promise<{Object}>}
	 */
	async getLastestHistorianData(limit = 100) {
		let res
		try {
			res = await this.index.get(`historian/get/latest/${limit}`)
		} catch (err) {
			return {success: false, error: err}
		}
		if (res && res.data) {
			let results = res.data.results
			return {success: true, results}
		} else {
			return {success: false, response: res.data, error: "No data returned from axios request"}
		}
	}

	/**
	 * Get OIP Daemon specs
	 * @return {Promise<Object>}
	 */
	async getVersion() {
		let res
		try {
			res = await this.index.get('/version')
		} catch (err) {
			return {success: false, error: "Missing axios data response", response: res}
		}
		return res.data
	}
}

export default OIPAPI;