import { athenaConfig } from '../../../global/athena-config.js';
import { isAppleDevice, isBrave } from '../../../global/helpers/athena-global-helper.js';

import {
    showMicrophoneIcon,
    openVoiceRecognitionAutocomplete,
    closeVoiceRecognitionAutocomplete,
    startVoiceRecognitionAnimation,
    stopVoiceRecognitionAnimation,
    showErrorMessage,
    getSearchResultText,
    getStopElement,
    appendQueryToSearchInput
} from './helper.js';

/*
|--------------------------------------------------------------------------
| Voice Recognition AKA Voice Search Singleton Class
|--------------------------------------------------------------------------
|
| This class serves as the main entry point of Voice Search.
|
| - Initializing core components of voice search
| - Error Handling
| - Normal and Voice Search with Search Result prepend
|
*/
class VoiceRecognition
{
    static #instance = null;

    #voiceRecognition = null;
    #shouldPrependSearchResults = false;

    static getInstance()
    {
        if (!VoiceRecognition.#instance)
            VoiceRecognition.#instance = new VoiceRecognition();

        return VoiceRecognition.#instance;
    }

    async initialize()
    {
        const SpeechRecognition =
            window.SpeechRecognition ||
            window.webkitSpeechRecognition;

        window.addEventListener('finishedConfiguration', async () => {
            if(
                !athenaConfig.voiceSearchStatus ||
                !await this.#isMicrophoneAccessible() ||

                !SpeechRecognition ||
                typeof SpeechRecognition === 'undefined' ||

                await isBrave()
            ) {
                return;
            }

            showMicrophoneIcon();

            this.#voiceRecognition = new SpeechRecognition();
            this.#voiceRecognition.continuous = false;
            this.#voiceRecognition.interimResults = true;
            this.#voiceRecognition.lang = athenaConfig.voiceSearchLang;

            this.#voiceRecognition.onstart = () => startVoiceRecognitionAnimation();
            this.#voiceRecognition.onresult = (event) => this.#triggerSearch(event);
            this.#voiceRecognition.onerror = (event) => this.#handleErrors(event);

            this.#handleStopOnElementClick();
        });
    }

    addVoiceRecognitionListener(element = null)
    {
        const voiceRecognitionElement = document.querySelector(`${element}`);
        voiceRecognitionElement?.addEventListener('click', (event) => {
            this.#shouldPrependSearchResults = event.currentTarget.getAttribute('data-should-prepend-search-results') === 'true';

            this.#appendAudioAutocompleteText();
            openVoiceRecognitionAutocomplete();
            this.#voiceRecognition?.start();
        });
    }

    disable(withoutClosing = false)
    {
        if(!athenaConfig.voiceSearchStatus)
            return;

        this.#voiceRecognition?.stop();
        if(withoutClosing) return;

        stopVoiceRecognitionAnimation();
        closeVoiceRecognitionAutocomplete();
    }

    async #isMicrophoneAccessible()
    {
        try {
            const permissionStatus = await navigator.permissions.query({ name: 'microphone' });

            if (permissionStatus.state === 'denied')
                return false;

            const devices = await navigator.mediaDevices.enumerateDevices();
            const microphone = devices.filter(device => device.kind === 'audioinput');

            return microphone.length > 0;
        } catch (error) {
            return false;
        }
    }

    #appendAudioAutocompleteText()
    {
        const audioSearchHeading = document.querySelector('.audio-search-heading');
        if(!audioSearchHeading) return;

        if(this.#shouldPrependSearchResults) {
            const searchResultText = getSearchResultText();
            audioSearchHeading.innerHTML = `
                    Continue describing for <div class="continuous-voice-text">${searchResultText}</div>
                `;
        } else {
            audioSearchHeading.innerHTML = `Tell Us What You're Looking For`;
        }
    }

    #handleStopOnElementClick()
    {
        const stopElement = getStopElement();
        stopElement?.addEventListener('click', () => {
            this.disable();
        });
    }

    #triggerSearch(event)
    {
        let transcript = '';
        for (let i = event.resultIndex; i < event.results.length; i++) {
            transcript += event.results[i][0].transcript;
            if (event.results[i].isFinal) {
                if(this.#shouldPrependSearchResults) {
                    const searchResultText = getSearchResultText();
                    transcript = `${searchResultText} ${transcript}`;
                }

                stopVoiceRecognitionAnimation();
                window.location.href = `${athenaConfig.formUrl}?q=${encodeURIComponent(transcript)}&conversion-channel=voice`;
                sessionStorage.setItem('athenaVoiceConversionChannel', 'true');
            } else {
                appendQueryToSearchInput(transcript);
            }
        }
    }

    #handleErrors(event)
    {
        if(event.error === 'service-not-allowed' && isAppleDevice()) {
            showErrorMessage('Please enable Speech Recognition access under: System Settings > Privacy & Security < Speech Recognition to use this feature.');
            this.disable(true);
            return;
        }

        if(event.error === 'no-speech') {
            showErrorMessage('Sorry we didn`t catch that. Please try again.');
            this.disable(true);
            setTimeout(() => {
                this.#voiceRecognition?.start();
            }, 1000);
            return;
        }

        console.error(`Athena XAI error: ${event.error} | trace: Voice Search Module`);
        this.disable();
    }
}

export default VoiceRecognition;