import async from "async";

import isLoginRequired from "../hooks/loginRequired";
import { useHasPermission } from "../hooks/hasPermission";

// eslint-disable-next-line
import moduleManager from "../../index";

const DBModule = moduleManager.modules.db;
const UtilsModule = moduleManager.modules.utils;
const SoundcloudModule = moduleManager.modules.soundcloud;
const CacheModule = moduleManager.modules.cache;

export default {
	/**
	 * Fetches new SoundCloud API key
	 * @returns {{status: string, data: object}}
	 */
	fetchNewApiKey: useHasPermission("soundcloud.fetchNewApiKey", async function fetchNewApiKey(session, cb) {
		this.keepLongJob();
		this.publishProgress({
			status: "started",
			title: "Fetch new SoundCloud API key",
			message: "Fetching new SoundCloud API key.",
			id: this.toString()
		});
		await CacheModule.runJob("RPUSH", { key: `longJobs.${session.userId}`, value: this.toString() }, this);
		await CacheModule.runJob(
			"PUB",
			{
				channel: "longJob.added",
				value: { jobId: this.toString(), userId: session.userId }
			},
			this
		);

		SoundcloudModule.runJob("GENERATE_SOUNDCLOUD_API_KEY", {}, this)
			.then(response => {
				this.log("SUCCESS", "SOUNDCLOUD_FETCH_NEW_API_KEY", `Fetching new API key was successful.`);
				this.publishProgress({
					status: "success",
					message: "Successfully fetched new SoundCloud API key."
				});
				return cb({
					status: "success",
					data: { response }
				});
			})
			.catch(async err => {
				err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
				this.log("ERROR", "SOUNDCLOUD_FETCH_NEW_API_KEY", `Fetching new API key failed. "${err}"`);
				this.publishProgress({
					status: "error",
					message: err
				});
				return cb({ status: "error", message: err });
			});
	}),

	/**
	 * Tests SoundCloud API key
	 * @returns {{status: string, data: object}}
	 */
	testApiKey: useHasPermission("soundcloud.testApiKey", async function testApiKey(session, cb) {
		this.keepLongJob();
		this.publishProgress({
			status: "started",
			title: "Test SoundCloud API key",
			message: "Testing SoundCloud API key.",
			id: this.toString()
		});
		await CacheModule.runJob("RPUSH", { key: `longJobs.${session.userId}`, value: this.toString() }, this);
		await CacheModule.runJob(
			"PUB",
			{
				channel: "longJob.added",
				value: { jobId: this.toString(), userId: session.userId }
			},
			this
		);

		SoundcloudModule.runJob("TEST_SOUNDCLOUD_API_KEY", {}, this)
			.then(response => {
				this.log(
					"SUCCESS",
					"SOUNDCLOUD_TEST_API_KEY",
					`Testing API key was successful. Response: ${response}.`
				);
				this.publishProgress({
					status: "success",
					message: "Successfully tested SoundCloud API key."
				});
				return cb({
					status: "success",
					data: { status: response.status }
				});
			})
			.catch(async err => {
				err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
				this.log("ERROR", "SOUNDCLOUD_TEST_API_KEY", `Testing API key failed. "${err}"`);
				this.publishProgress({
					status: "error",
					message: err
				});
				return cb({ status: "error", message: err });
			});
	}),

	/**
	 * Get a Soundcloud artist from ID
	 * @returns {{status: string, data: object}}
	 */
	getArtist: useHasPermission("soundcloud.getArtist", function getArtist(session, userPermalink, cb) {
		return SoundcloudModule.runJob("GET_ARTISTS_FROM_PERMALINKS", { userPermalinks: [userPermalink] }, this)
			.then(res => {
				if (res.artists.length === 0) {
					this.log("ERROR", "SOUNDCLOUD_GET_ARTISTS_FROM_PERMALINKS", `Fetching artist failed.`);
					return cb({ status: "error", message: "Failed to get artist" });
				}

				this.log("SUCCESS", "SOUNDCLOUD_GET_ARTISTS_FROM_PERMALINKS", `Fetching artist was successful.`);
				return cb({
					status: "success",
					message: "Successfully fetched Soundcloud artist",
					data: res.artists[0]
				});
			})
			.catch(async err => {
				err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
				this.log("ERROR", "SOUNDCLOUD_GET_ARTISTS_FROM_PERMALINKS", `Fetching artist failed. "${err}"`);
				return cb({ status: "error", message: err });
			});
	}),

	/**
	 * Gets videos, used in the admin youtube page by the AdvancedTable component
	 * @param {object} session - the session object automatically added by the websocket
	 * @param page - the page
	 * @param pageSize - the size per page
	 * @param properties - the properties to return for each news item
	 * @param sort - the sort object
	 * @param queries - the queries array
	 * @param operator - the operator for queries
	 * @param cb
	 */
	getTracks: useHasPermission(
		"admin.view.soundcloudTracks",
		async function getTracks(session, page, pageSize, properties, sort, queries, operator, cb) {
			async.waterfall(
				[
					next => {
						DBModule.runJob(
							"GET_DATA",
							{
								page,
								pageSize,
								properties,
								sort,
								queries,
								operator,
								modelName: "soundcloudTrack",
								blacklistedProperties: [],
								specialProperties: {
									songId: [
										// Fetch songs from songs collection with a matching mediaSource, which we first need to assemble
										{
											$lookup: {
												from: "songs",
												let: {
													mediaSource: { $concat: ["soundcloud:", { $toString: "$trackId" }] }
												},
												pipeline: [
													{
														$match: {
															$expr: { $eq: ["$mediaSource", "$$mediaSource"] }
														}
													}
												],
												as: "song"
											}
										},
										// Turn the array of songs returned in the last step into one object, since only one song should have been returned maximum
										{
											$unwind: {
												path: "$song",
												preserveNullAndEmptyArrays: true
											}
										},
										// Add new field songId, which grabs the song object's _id and tries turning it into a string
										{
											$addFields: {
												songId: {
													$convert: {
														input: "$song._id",
														to: "string",
														onError: "",
														onNull: ""
													}
												}
											}
										},
										// Cleanup, don't return the song object for any further steps
										{
											$project: {
												song: 0
											}
										}
									]
								},
								specialQueries: {},
								specialFilters: {}
							},
							this
						)
							.then(response => {
								next(null, response);
							})
							.catch(err => {
								next(err);
							});
					}
				],
				async (err, response) => {
					if (err && err !== true) {
						err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
						this.log("ERROR", "SOUNDCLOUD_GET_TRACKS", `Failed to get SoundCloud tracks. "${err}"`);
						return cb({ status: "error", message: err });
					}
					this.log("SUCCESS", "SOUNDCLOUD_GET_TRACKS", `Fetched SoundCloud tracks successfully.`);
					return cb({
						status: "success",
						message: "Successfully fetched SoundCloud tracks.",
						data: response
					});
				}
			);
		}
	),

	/**
	 * Get a SoundCloud track
	 * @param {object} session - the session object automatically added by the websocket
	 * @param identifier - the identifier of the SoundCloud track
	 * @param createMissing - whether to create/fetch the SoundCloud track if it's missing
	 * @returns {{status: string, data: object}}
	 */
	getTrack: isLoginRequired(function getTrack(session, identifier, createMissing, cb) {
		return SoundcloudModule.runJob("GET_TRACK", { identifier, createMissing }, this)
			.then(res => {
				this.log("SUCCESS", "SOUNDCLOUD_GET_TRACK", `Fetching track was successful.`);

				return cb({ status: "success", message: "Successfully fetched SoundCloud track", data: res.track });
			})
			.catch(async err => {
				err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
				this.log("ERROR", "SOUNDCLOUD_GET_TRACK", `Fetching track failed. "${err}"`);
				return cb({ status: "error", message: err });
			});
	})
};
