import {JITSI_URL} from "../Enum/EnvironmentVariable";
import {mediaManager} from "./MediaManager";
import {coWebsiteManager} from "./CoWebsiteManager";
import {requestedCameraState, requestedMicrophoneState} from "../Stores/MediaStore";
import {get} from "svelte/store";
declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any

interface jitsiConfigInterface {
    startWithAudioMuted: boolean
    startWithVideoMuted: boolean
    prejoinPageEnabled: boolean
}

const getDefaultConfig = () : jitsiConfigInterface => {
    return {
        startWithAudioMuted: !get(requestedMicrophoneState),
        startWithVideoMuted: !get(requestedCameraState),
        prejoinPageEnabled: false
    }
}

const mergeConfig = (config?: object) => {
    const currentDefaultConfig = getDefaultConfig();
    if(!config){
        return currentDefaultConfig;
    }
    return {
        ...currentDefaultConfig,
        ...config,
        startWithAudioMuted: (config as jitsiConfigInterface).startWithAudioMuted ? true : currentDefaultConfig.startWithAudioMuted,
        startWithVideoMuted: (config as jitsiConfigInterface).startWithVideoMuted ? true : currentDefaultConfig.startWithVideoMuted,
        prejoinPageEnabled: (config as jitsiConfigInterface).prejoinPageEnabled ? true : currentDefaultConfig.prejoinPageEnabled
    }
}

const defaultInterfaceConfig = {
    SHOW_CHROME_EXTENSION_BANNER: false,
    MOBILE_APP_PROMO: false,

    HIDE_INVITE_MORE_HEADER: true,
    DISABLE_JOIN_LEAVE_NOTIFICATIONS: true,
    DISABLE_VIDEO_BACKGROUND: true,

    // Note: hiding brand does not seem to work, we probably need to put this on the server side.
    SHOW_BRAND_WATERMARK: false,
    SHOW_JITSI_WATERMARK: false,
    SHOW_POWERED_BY: false,
    SHOW_PROMOTIONAL_CLOSE_PAGE: false,
    SHOW_WATERMARK_FOR_GUESTS: false,

    TOOLBAR_BUTTONS: [
        'microphone', 'camera', 'closedcaptions', 'desktop', /*'embedmeeting',*/ 'fullscreen',
        'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
        'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
        'videoquality', 'filmstrip', /*'invite',*/ 'feedback', 'stats', 'shortcuts',
        'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', /*'security'*/
    ],
};

const slugify = (...args: (string | number)[]): string => {
    const value = args.join(' ')

    return value
        .normalize('NFD') // split an accented letter in the base letter and the accent
        .replace(/[\u0300-\u036f]/g, '') // remove all previously split accents
        .toLowerCase()
        .trim()
        .replace(/[^a-z0-9 ]/g, '') // remove all chars not letters, numbers and spaces (to be replaced)
        .replace(/\s+/g, '-') // separator
}

class JitsiFactory {
    private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any
    private audioCallback = this.onAudioChange.bind(this);
    private videoCallback = this.onVideoChange.bind(this);
    private jitsiScriptLoaded: boolean = false;

    /**
     * Slugifies the room name and prepends the room name with the instance
     */
    public getRoomName(roomName: string, instance: string): string {
        return slugify(instance.replace('/', '-') + "-" + roomName);
    }

    public start(roomName: string, playerName:string, jwt?: string, config?: object, interfaceConfig?: object, jitsiUrl?: string): void {
        coWebsiteManager.insertCoWebsite((async cowebsiteDiv => {
            // Jitsi meet external API maintains some data in local storage
            // which is sent via the appData URL parameter when joining a
            // conference. Problem is that this data grows indefinitely. Thus
            // after some time the URLs get so huge that loading the iframe
            // becomes slow and eventually breaks completely. Thus lets just
            // clear jitsi local storage before starting a new conference.
            window.localStorage.removeItem("jitsiLocalStorage");

            const domain = jitsiUrl || JITSI_URL;
            if (domain === undefined) {
                throw new Error('Missing JITSI_URL environment variable or jitsiUrl parameter in the map.')
            }
            await this.loadJitsiScript(domain);

            const options: any = { // eslint-disable-line @typescript-eslint/no-explicit-any
                roomName: roomName,
                jwt: jwt,
                width: "100%",
                height: "100%",
                parentNode: cowebsiteDiv,
                configOverwrite: mergeConfig(config),
                interfaceConfigOverwrite: {...defaultInterfaceConfig, ...interfaceConfig}
            };
            if (!options.jwt) {
                delete options.jwt;
            }

            return new Promise((resolve, reject) => {
                options.onload = () => resolve(); //we want for the iframe to be loaded before triggering animations.
                setTimeout(() => resolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load
                this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options);
                this.jitsiApi.executeCommand('displayName', playerName);

                this.jitsiApi.addListener('audioMuteStatusChanged', this.audioCallback);
                this.jitsiApi.addListener('videoMuteStatusChanged', this.videoCallback);
            });
        }));
    }

    public async stop(): Promise<void> {
        if(!this.jitsiApi){
            return;
        }
        await coWebsiteManager.closeCoWebsite();
        this.jitsiApi.removeListener('audioMuteStatusChanged', this.audioCallback);
        this.jitsiApi.removeListener('videoMuteStatusChanged', this.videoCallback);
        this.jitsiApi?.dispose();
    }

    private onAudioChange({muted}: {muted: boolean}): void {
        if (muted) {
            requestedMicrophoneState.disableMicrophone();
        } else {
            requestedMicrophoneState.enableMicrophone();
        }
    }

    private onVideoChange({muted}: {muted: boolean}): void {
        if (muted) {
            requestedCameraState.disableWebcam();
        } else {
            requestedCameraState.enableWebcam();
        }
    }

    private async loadJitsiScript(domain: string): Promise<void> {
        return new Promise<void>((resolve, reject) => {
            if (this.jitsiScriptLoaded) {
                resolve();
                return;
            }

            this.jitsiScriptLoaded = true;

            // Load Jitsi if the environment variable is set.
            const jitsiScript = document.createElement('script');
            jitsiScript.src = 'https://' + domain + '/external_api.js';
            jitsiScript.onload = () => {
                resolve();
            }
            jitsiScript.onerror = () => {
                reject();
            }

            document.head.appendChild(jitsiScript);

        })


    }
}

export const jitsiFactory = new JitsiFactory();
