import { ref, watch } from "vue";

const TAG = "[USP]";

const soundcloudDomain = "https://w.soundcloud.com";

export const useSoundcloudPlayer = () => {
	console.debug(TAG, "Init start");

	const soundcloudIframeElement = ref();
	const widgetId = ref();
	const volume = ref();
	const readyCallback = ref();
	const attemptsToPlay = ref(0);

	const playAttemptTimeout = ref();

	const paused = ref(true);
	const currentTrackId = ref(null);

	const methodCallbacks = {};
	const eventListenerCallbacks = {};
	const stateChangeCallbacks = [];

	/*
	EVENTS:
		LOAD_PROGRESS: "loadProgress",
		PLAY_PROGRESS: "playProgress",
		PLAY: "play",
		PAUSE: "pause",
		FINISH: "finish",
		SEEK: "seek",
		READY: "ready",
		OPEN_SHARE_PANEL: "sharePanelOpened",
		CLICK_DOWNLOAD: "downloadClicked",
		CLICK_BUY: "buyClicked",
		ERROR: "error"

	METHODS:
		GET_VOLUME: "getVolume",
		GET_DURATION: "getDuration",
		GET_POSITION: "getPosition",
		GET_SOUNDS: "getSounds",
		GET_CURRENT_SOUND: "getCurrentSound",
		GET_CURRENT_SOUND_INDEX: "getCurrentSoundIndex",
		IS_PAUSED: "isPaused"

		PLAY: "play",
		PAUSE: "pause",
		TOGGLE: "toggle",
		SEEK_TO: "seekTo",
		SET_VOLUME: "setVolume",
		NEXT: "next",
		PREV: "prev",
		SKIP: "skip"

		REMOVE_LISTENER: "removeEventListener",
		ADD_LISTENER: "addEventListener"
	*/

	const trackState = ref("NOT_PLAYING");

	const dispatchMessage = (method, value = null) => {
		const payload = {
			method,
			value
		};

		if (!soundcloudIframeElement.value) return;
		if (
			!soundcloudIframeElement.value.src ||
			!soundcloudIframeElement.value.src.startsWith(soundcloudDomain)
		)
			return;

		if (method !== "getPosition" && method !== "getDuration")
			console.debug(TAG, "Dispatch message", method, value);

		soundcloudIframeElement.value.contentWindow.postMessage(
			JSON.stringify(payload),
			`${soundcloudDomain}/player`
		);
	};

	const onLoadListener = () => {};

	const onMessageListener = event => {
		if (event.origin !== soundcloudDomain) return;

		const data = JSON.parse(event.data);
		if (data.method !== "getPosition" && data.method !== "getDuration")
			console.log("MESSAGE DATA", data);

		if (data.method === "ready") {
			widgetId.value = data.widgetId;

			if (!readyCallback.value) return;

			readyCallback.value();

			return;
		}

		if (methodCallbacks[data.method]) {
			methodCallbacks[data.method].forEach(callback => {
				callback(data.value);
			});
			methodCallbacks[data.method] = [];
		}

		if (eventListenerCallbacks[data.method]) {
			eventListenerCallbacks[data.method].forEach(callback => {
				callback(data.value);
			});
		}
	};

	const addMethodCallback = (type, cb) => {
		if (!methodCallbacks[type]) methodCallbacks[type] = [];
		methodCallbacks[type].push(cb);
	};

	const changeTrackState = newTrackState => {
		const oldTrackState = trackState.value;

		trackState.value = newTrackState;

		if (newTrackState !== oldTrackState) {
			stateChangeCallbacks.forEach(cb => {
				cb(newTrackState);
			});
		}
	};

	const soundcloudGetIsPaused = callback => {
		let called = false;

		const _callback = value => {
			if (called) return;
			called = true;

			callback(value);
		};
		addMethodCallback("isPaused", _callback);

		dispatchMessage("isPaused");
	};

	const attemptToPlay = () => {
		if (trackState.value === "playing") return;

		if (trackState.value !== "attempting_to_play") {
			attemptsToPlay.value = 0;
			changeTrackState("attempting_to_play");
		}

		attemptsToPlay.value += 1;

		dispatchMessage("play");
		setTimeout(() => {
			soundcloudGetIsPaused(value => {
				if (trackState.value !== "attempting_to_play") return;

				// Success
				if (!value) {
					changeTrackState("playing");
					return;
				}

				// Too many attempts, failed
				if (attemptsToPlay.value >= 10 && value && !paused.value) {
					changeTrackState("failed_to_play");
					attemptsToPlay.value = 0;
					return;
				}

				if (playAttemptTimeout.value)
					clearTimeout(playAttemptTimeout.value);
				playAttemptTimeout.value = setTimeout(() => {
					if (trackState.value === "attempting_to_play")
						attemptToPlay();
				}, 500);
			});
		}, 500);
	};

	watch(soundcloudIframeElement, (newElement, oldElement) => {
		if (oldElement) {
			oldElement.removeEventListener("load", onLoadListener);

			window.removeEventListener("message", onMessageListener);
		}

		if (newElement) {
			newElement.addEventListener("load", onLoadListener);

			window.addEventListener("message", onMessageListener);
		}
	});

	/* Exported functions */

	const soundcloudPlay = () => {
		paused.value = false;

		console.debug(TAG, "Soundcloud play");

		if (trackState.value !== "attempting_to_play") attemptToPlay();
	};

	const soundcloudPause = () => {
		paused.value = true;

		console.debug(TAG, "Soundcloud pause");

		if (playAttemptTimeout.value) clearTimeout(playAttemptTimeout.value);

		dispatchMessage("pause");
	};

	const soundcloudSetVolume = _volume => {
		volume.value = _volume;

		console.debug(TAG, "Soundcloud set volume");

		dispatchMessage("setVolume", _volume);
	};

	const soundcloudSeekTo = time => {
		console.log("SC SEEK TO", time);

		console.debug(TAG, "Soundcloud seek to");

		dispatchMessage("seekTo", time);
	};

	const soundcloudGetPosition = callback => {
		let called = false;

		const _callback = value => {
			if (called) return;
			called = true;

			callback(value);
		};
		addMethodCallback("getPosition", _callback);

		dispatchMessage("getPosition");
	};

	const soundcloudGetDuration = callback => {
		let called = false;

		const _callback = value => {
			if (called) return;
			called = true;

			callback(value);
		};
		addMethodCallback("getDuration", _callback);

		dispatchMessage("getDuration");
	};

	const soundcloudGetState = () => trackState.value;

	const soundcloudGetCurrentSound = callback => {
		let called = false;

		const _callback = value => {
			if (called) return;
			called = true;

			callback(value);
		};
		addMethodCallback("getCurrentSound", _callback);

		dispatchMessage("getCurrentSound");
	};

	const soundcloudGetTrackId = () => currentTrackId.value;

	const soundcloudLoadTrack = (trackId, startTime, _paused) => {
		if (!soundcloudIframeElement.value) return;

		console.debug(TAG, "Soundcloud load track");

		const url = `${soundcloudDomain}/player?autoplay=false&buying=false&sharing=false&download=false&show_artwork=false&show_playcount=false&show_user=false&url=${`https://api.soundcloud.com/tracks/${trackId}`}`;

		soundcloudIframeElement.value.setAttribute("src", url);

		paused.value = _paused;
		currentTrackId.value = trackId;

		if (playAttemptTimeout.value) clearTimeout(playAttemptTimeout.value);

		changeTrackState("not_started");

		readyCallback.value = () => {
			Object.keys(eventListenerCallbacks).forEach(event => {
				dispatchMessage("addEventListener", event);
			});

			dispatchMessage("setVolume", volume.value ?? 20);
			dispatchMessage("seekTo", (startTime ?? 0) * 1000);
			if (!_paused) attemptToPlay();
		};
	};

	const soundcloudBindListener = (name, callback) => {
		if (!eventListenerCallbacks[name]) {
			eventListenerCallbacks[name] = [];
			dispatchMessage("addEventListener", name);
		}

		eventListenerCallbacks[name].push(callback);
	};

	const soundcloudOnTrackStateChange = callback => {
		console.debug(TAG, "On track state change listener added");
		stateChangeCallbacks.push(callback);
	};

	const soundcloudDestroy = () => {
		if (!soundcloudIframeElement.value) return;

		const url = `${soundcloudDomain}/player?autoplay=false&buying=false&sharing=false&download=false&show_artwork=false&show_playcount=false&show_user=false&url=${`https://api.soundcloud.com/tracks/${0}`}`;
		soundcloudIframeElement.value.setAttribute("src", url);

		currentTrackId.value = null;

		changeTrackState("none");
	};

	const soundcloudUnload = () => {
		window.removeEventListener("message", onMessageListener);
	};

	soundcloudBindListener("play", () => {
		console.debug(TAG, "On play");

		if (trackState.value !== "attempting_to_play")
			changeTrackState("playing");
	});

	soundcloudBindListener("pause", eventValue => {
		console.debug(TAG, "On pause", eventValue);

		const finishedPlaying = eventValue.relativePosition === 1;
		if (finishedPlaying) {
			changeTrackState("finished");
			return;
		}

		if (trackState.value !== "attempting_to_play")
			changeTrackState("paused");
	});

	soundcloudBindListener("finish", () => {
		console.debug(TAG, "On finish");

		changeTrackState("finished");
	});

	soundcloudBindListener("error", () => {
		console.debug(TAG, "On error");

		changeTrackState("error");
	});

	console.debug(TAG, "Init end");

	return {
		soundcloudIframeElement,
		soundcloudPlay,
		soundcloudPause,
		soundcloudSeekTo,
		soundcloudSetVolume,
		soundcloudLoadTrack,
		soundcloudGetPosition,
		soundcloudGetDuration,
		soundcloudGetIsPaused,
		soundcloudGetState,
		soundcloudGetTrackId,
		soundcloudGetCurrentSound,
		soundcloudBindListener,
		soundcloudOnTrackStateChange,
		soundcloudDestroy,
		soundcloudUnload
	};
};
