'use strict';

import roomSession from './room-session';
import streamingSession from "./streaming-session";
import {decodeJanusDisplay} from "./utils";

let room = function () {

	let roomSessions = [];

	let setExitListeners = () => {
		window.addEventListener('pagehide', (event) => {
			if (!event.persisted) {
				this.room.destroySessions()
			}
		});
		window.addEventListener('beforeunload', () => {
			this.room.destroySessions()
		});
	}

	setExitListeners();

	return {

		//TODO:deprecated
		integration: (data = {}) => {
			return this.__privates.auth.__client
				.then(client => client.apis.system.integration({type: this.__instanceType}, {
					requestBody: {...data}, requestInterceptor: (req) => {
						if (!req.headers) {
							req.headers = {};
						}
						req.headers['Authorization'] = 'Bearer ' + localStorage.getItem('rwt_idToken');
						return req;
					}
				}));
		},

		//TODO:deprecated
		getAnalytics: (data = {}) => {
			return this.__privates.auth.__client
				.then(client => client.apis.system.getAnalytics({instanceType: this.__instanceType, ...data}))
		},

		sendChatMessage: ({roomId, message, options, senderUserId}) => {
			return this.__privates.auth.__client
				.then(client => client.apis.wt.sendRoomMessage({}, {
					requestBody: {
						roomId,
						message,
						options,
						senderUserId
					}
				}))
		},

		createRoom: ({sourceRoomId, title, description, isPublic, isRouter, isStudioLayout, wtChannelId, isHd, disableSync, reduceRoomControls, hasStudioChat, maxParticipants, customAttributes, chatRoomId, linkedRoomId, type, dotAttribute} = {}) => {
			let _da = dotAttribute
				? (Array.isArray(dotAttribute)
					? dotAttribute.reduce((p, cv) => (p[cv.name] = cv.value) && p || p, {})
					: {[dotAttribute.name]: dotAttribute.value})
				: {};
			return this.__privates.auth.__client
				.then(client => client.apis.wt.createRoom({
					sourceRoomId
				}, {
					requestBody: {
						title,
						description,
						isRouter,
						isPublic,
						isStudioLayout,
						wtChannelId,
						isHd,
						disableSync,
						reduceRoomControls,
						chatRoomId,
						linkedRoomId,
						hasStudioChat,
						maxParticipants,
						type,
						customAttributes,
						..._da,
						instanceType: this.__instanceType,
					}
				}))
		},

		updateRoom: ({roomId, title, description, isPublic, isRouter, recordings, slug, password, maxParticipants, setInstanceType = false, wtChannelId, isHd, isStudioLayout, hasStudioChat, reduceRoomControls, chatRoomId, linkedRoomId, type, disableSync, defaultRegion, isSimulcast, isLongpolling, isHostAudioOnly, videoCodec, customAttributes, dotAttribute} = {}) => {
			let _da = dotAttribute
				? (Array.isArray(dotAttribute)
					? dotAttribute.reduce((p, cv) => (p[cv.name] = cv.value) && p || p, {})
					: {[dotAttribute.name]: dotAttribute.value})
				: {};
			return this.__privates.auth.__client
				.then(client => client.apis.wt.updateRoom({id: roomId}, {
					requestBody: {
						title,
						description,
						slug,
						password,
						isPublic,
						isRouter,
						isHd,
						isStudioLayout,
						hasStudioChat,
						maxParticipants,
						reduceRoomControls,
						chatRoomId,
						linkedRoomId,
						type,
						disableSync,
						defaultRegion,
						isSimulcast,
						isLongpolling,
						isHostAudioOnly,
						videoCodec,
						customAttributes,
						wtChannelId,
						..._da,
						...(setInstanceType && {instanceType: setInstanceType === true ? this.__instanceType : setInstanceType}),
						...(recordings && {recordings})
					}
				}))
		},

		deleteRoom: (roomId) => {
			return this.__privates.auth.__client
				.then(client => client.apis.wt.deleteRoom({id: roomId}))
		},

		leaveRoom: (roomId) => {
			return this.__privates.auth.__client
				.then(client => client.apis.wt.leave({roomId}))
		},

		updateLayout: ({roomId, operation, name, attributeName, value, target}) => {

			//operation : clearLayouts, setLayout, setLayouts, unsetLayout, setName, setTarget, setStyle, setAttribute, unsetAttribute,'sendMessage'

			return this.__privates.auth.__client
				.then(client => client.apis.wt.updateLayout({}, {
					requestBody: {
						roomId,
						operation,
						name,
						attributeName,
						value,
						target
					}
				}))
		},

		updateAttribute: ({roomId, id, deviceId, operation, attributeName, value}) => {
			return this.__privates.auth.__client
				.then(client => client.apis.wt.updateAttribute({}, {
					requestBody: {
						id,
						deviceId,
						roomId,
						operation,
						attributeName,
						value,
					}
				}))
		},

		getInviteUrl: ({roomId, domain, url, autologin}) => {
			return this.__privates.auth.__client
				.then(client => client.apis.wt.invite({}, {
					requestBody: {
						roomId,
						domain: domain || location.host,
						url: url || location.href,
						...(autologin && {autologin}),
					}
				}))
		},

		getRoomChatList: ({roomId, type = "normal", size = 50, startKey = null} = {}) => {
			let apiParams = {
				roomId, type,
				size,
				...(startKey && {startKey})
			};
			return this.__privates.auth.__client
				.then(client => client.apis.wt.getRoomChatList(apiParams))
		},

		getRoomsList: ({ type = 'participant', activeOnly = null, instanceType = this.__instanceType, size = 20, startKey = null, includeWtEventModels = false, demo = false, viewType = 'list', wtChannelId = undefined} = {}) => {
			let apiParams = {
				type, instanceType,
				size,
				includeWtEventModels,
				demo,
				viewType,
				...(wtChannelId && {wtChannelId}),
				...(activeOnly && {activeOnly}),
				...(startKey && {startKey})
			};

			return this.__privates.auth.__client
				.then(client => client.apis.wt.getRoomList(apiParams))

		},

		setUser: ({messageId, userId, roomId, flag, timestamp, option}) => {
			//leave, kick, ban, unban, approve, report
			return this.__privates.auth.__client
				.then(client => client.apis.wt.setUser({messageId, userId, roomId, flag, timestamp, option}));
		},

		getRoomById: (id, pinHash = undefined, showPublic = undefined, demo = false, signal) => {
			return this.__privates.auth.__client
				.then(client => client.apis.wt.getRoom({id, pinHash, "public": showPublic, demo}, {
					...(signal ? {
					requestInterceptor: (req) => {
						req.signal = signal;
						return req;
					}
				} : {}),
			}))
		},

		getRoomBySlug: (slug, password = undefined, showPublic = undefined, demo = false) => {
			return this.__privates.auth.__client
				.then(client => client.apis.wt.getRoom({slug, password, "public": showPublic, demo}))

		},

		roomRecorder: ({roomId, operation = 'get', config, outputs, destinationIds, destinations, screenWidth, screenHeight, payload, requestId} = {}) => {
			return this.__privates.auth.__client
				.then(client => client.apis.wt.recordRoom({}, {
					requestBody: {
						roomId, operation, config, outputs, destinationIds, destinations, screenWidth, screenHeight, payload, requestId
					}
				}))
		},

		__joinRoom: ({roomId, pinHash, instanceType = this.__instanceType, role = 'participant', customBearer = undefined} = {}, signal) => {
			return this.__privates.auth.__client
				.then(client => client.apis.wt.join({
					roomId,
					pinHash,
					instanceType,
					role,
					platform: this.browser === 'firefox' ? 'web-firefox' : 'web'
				}, {
					...(signal ? {
						requestInterceptor: (req) => {
							req.signal = signal;
							return req;
						}
					} : {}),
				}))
		},

		isSafariSupported: () => {
			return this.__privates.room.safariVp8;
		},

		isWebrtcSupported: () => {
			return this.__privates.room.isWebrtcSupported;
		},

		createStreamingSession: ({constructId, roomId, pinHash, streamId, href, iceServers, accessToken} = {}) => {
			return this.__privates.room.whenInitialized
				.then(lib => streamingSession.call(this, {
					roomId,
					pinHash,
					streamId,
					href,
					iceServers,
					accessToken
				}, lib.createSession(constructId, 'streaming'), this))
				.then(newSession => {
					let existingSessionIndex = roomSessions.findIndex(session => session.constructId === newSession.constructId);
					if(existingSessionIndex > -1) {
						return this.room.destroySession(newSession.constructId)
							.then(() => {
								roomSessions.push(newSession);
								return newSession;
							});
					} else {
						roomSessions.push(newSession);
						return newSession;
					}
				})
		},

		createSession: ({constructId, roomId, pinHash, role, options}) => {
			return this.__privates.room.whenInitialized
				.then(lib => roomSession.call(this, {
					roomId,
					pinHash,
					role,
					options
				}, lib.createSession(constructId, 'reactooroom', options), this))
				.then(newSession => {
					let existingSessionIndex = roomSessions.findIndex(session => session.constructId === newSession.constructId);
					if(existingSessionIndex > -1) {
						return this.room.destroySession(newSession.constructId)
							.then(() => {
								roomSessions.push(newSession);
								return newSession;
							});
					} else {
						roomSessions.push(newSession);
						return newSession;
					}
				})
		},

		getSessions: () => {
			return roomSessions;
		},
		getSessionByConstructId: (constructId) => {
			return roomSessions.find(session => session.constructId === constructId) || null
		},
		getSessionBySessionId: (sessionId) => {
			return roomSessions.find(session => session.sessionId === sessionId) || null
		},
		getSessionByRoomId: (roomId) => {
			return roomSessions.find(session => session.roomId === roomId) || null
		},
		getSessionByUserId: (userId) => {
			return roomSessions.find(session => decodeJanusDisplay(session.userId)?.userId === userId) || null
		},
		getSessionByStreamId: (streamId) => {
			return roomSessions.find(session => session.streamId === streamId) || null
		},


		destroySession: (constructId) => {
				let sessionIndex = roomSessions.findIndex(session => session.constructId === constructId);
				if (sessionIndex > -1) {
					return roomSessions[sessionIndex].kill()
						.finally(() => {
							roomSessions.splice(sessionIndex, 1);
							return true
						})
				}
				return Promise.resolve();
		},

		destroySessionBySessionId: (sessionId) => {
			let sessionIndex = roomSessions.findIndex(session => session.sessionId === sessionId);
			if (sessionIndex > -1) {
				return roomSessions[sessionIndex].kill()
					.finally(() => {
						roomSessions.splice(sessionIndex, 1);
						return true
					})
			}
			return Promise.resolve();
		},

		destroySessions: () => {
			roomSessions.forEach(session => session.destroy().catch(e => this.__privates.room._log(e)));
			roomSessions.length = 0;
		},

		queue: ({roomId, operation, userId} = {}) => {
			//Enum: "status" "join" "approve" "reject" "block" "unblock"
			return this.__privates.auth.__client
				.then(client => client.apis.wt.queue({}, {
					requestBody: {
						roomId, operation, userId
					}
				}))
		},

		getDefaultRegions: () => {
			return this.__privates.auth.__client
				.then(client => [...(client.spec?.components?.schemas?.WtRoom?.properties?.defaultRegion?.enum || [''])])
		}


	}
};

export default room;
