import { IReduxState } from '../app/types';
import { IStateful } from '../base/app/types';
import { isNameReadOnly } from '../base/config/functions.any';
import { SERVER_URL_CHANGE_ENABLED } from '../base/flags/constants';
import { getFeatureFlag } from '../base/flags/functions';
import i18next, { DEFAULT_LANGUAGE, LANGUAGES } from '../base/i18n/i18next';
import { getLocalParticipant } from '../base/participants/functions';
import { toState } from '../base/redux/functions';
import { getHideSelfView } from '../base/settings/functions.any';
import { parseStandardURIString } from '../base/util/uri';
import { isStageFilmstripEnabled } from '../filmstrip/functions';
import { isFollowMeActive, isFollowMeRecorderActive } from '../follow-me/functions';
import { isPrejoinEnabledInConfig } from '../prejoin/functions';
import { isReactionsEnabled } from '../reactions/functions.any';
import { iAmVisitor } from '../visitors/functions';

import { shouldShowModeratorSettings } from './functions';

/**
 * Returns true if user is allowed to change Server URL.
 *
 * @param {(Function|Object)} stateful - The (whole) redux state, or redux's
 * {@code getState} function to be used to retrieve the state.
 * @returns {boolean} True to indicate that user can change Server URL, false otherwise.
 */
export function isServerURLChangeEnabled(stateful: IStateful) {
    const state = toState(stateful);

    return getFeatureFlag(state, SERVER_URL_CHANGE_ENABLED, true);
}

/**
 * Normalizes a URL entered by the user.
 * FIXME: Consider adding this to base/util/uri.
 *
 * @param {string} url - The URL to validate.
 * @returns {string|null} - The normalized URL, or null if the URL is invalid.
 */
export function normalizeUserInputURL(url: string) {
    /* eslint-disable no-param-reassign */

    if (url) {
        url = url.replace(/\s/g, '').toLowerCase();

        const urlRegExp = new RegExp('^(\\w+://)?(.+)$');
        const urlComponents = urlRegExp.exec(url);

        if (urlComponents && !urlComponents[1]?.startsWith('http')) {
            url = `https://${urlComponents[2]}`;
        }

        const parsedURI = parseStandardURIString(url);

        if (!parsedURI.host) {
            return null;
        }

        return parsedURI.toString();
    }

    return url;

    /* eslint-enable no-param-reassign */
}

/**
 * Returns the notification types and their user selected configuration.
 *
 * @param {(Function|Object)} stateful -The (whole) redux state, or redux's
 * {@code getState} function to be used to retrieve the state.
 * @returns {Object} - The section of notifications to be configured.
 */
export function getNotificationsMap(stateful: IStateful): { [key: string]: boolean; } {
    const state = toState(stateful);
    const { notifications } = state['features/base/config'];
    const { userSelectedNotifications } = state['features/base/settings'];

    if (!userSelectedNotifications) {
        return {};
    }

    return Object.keys(userSelectedNotifications)
        .filter(key => !notifications || notifications.includes(key))
        .reduce((notificationsMap, key) => {
            return {
                ...notificationsMap,
                [key]: userSelectedNotifications[key]
            };
        }, {});
}

/**
 * Returns the properties for the "More" tab from settings dialog from Redux
 * state.
 *
 * @param {(Function|Object)} stateful -The (whole) redux state, or redux's
 * {@code getState} function to be used to retrieve the state.
 * @returns {Object} - The properties for the "More" tab from settings dialog.
 */
export function getMoreTabProps(stateful: IStateful) {
    const state = toState(stateful);
    const stageFilmstripEnabled = isStageFilmstripEnabled(state);
    const language = i18next.language || DEFAULT_LANGUAGE;
    const configuredTabs: string[] = interfaceConfig.SETTINGS_SECTIONS || [];

    // when self view is controlled by the config we hide the settings
    const { disableSelfView, disableSelfViewSettings } = state['features/base/config'];

    return {
        currentLanguage: language,
        disableHideSelfView: disableSelfViewSettings || disableSelfView,
        hideSelfView: getHideSelfView(state),
        iAmVisitor: iAmVisitor(state),
        languages: LANGUAGES,
        maxStageParticipants: state['features/base/settings'].maxStageParticipants,
        showLanguageSettings: configuredTabs.includes('language'),
        showPrejoinPage: !state['features/base/settings'].userSelectedSkipPrejoin,
        showPrejoinSettings: isPrejoinEnabledInConfig(state),
        stageFilmstripEnabled
    };
}

/**
 * Returns the properties for the "More" tab from settings dialog from Redux
 * state.
 *
 * @param {(Function|Object)} stateful -The (whole) redux state, or redux's
 * {@code getState} function to be used to retrieve the state.
 * @returns {Object} - The properties for the "More" tab from settings dialog.
 */
export function getModeratorTabProps(stateful: IStateful) {
    const state = toState(stateful);
    const {
        conference,
        followMeEnabled,
        followMeRecorderEnabled,
        startAudioMutedPolicy,
        startVideoMutedPolicy,
        startReactionsMuted
    } = state['features/base/conference'];
    const { disableReactionsModeration } = state['features/base/config'];
    const followMeActive = isFollowMeActive(state);
    const followMeRecorderActive = isFollowMeRecorderActive(state);
    const showModeratorSettings = shouldShowModeratorSettings(state);

    // The settings sections to display.
    return {
        showModeratorSettings: Boolean(conference && showModeratorSettings),
        disableReactionsModeration: Boolean(disableReactionsModeration),
        followMeActive: Boolean(conference && followMeActive),
        followMeEnabled: Boolean(conference && followMeEnabled),
        followMeRecorderActive: Boolean(conference && followMeRecorderActive),
        followMeRecorderEnabled: Boolean(conference && followMeRecorderEnabled),
        startReactionsMuted: Boolean(conference && startReactionsMuted),
        startAudioMuted: Boolean(conference && startAudioMutedPolicy),
        startVideoMuted: Boolean(conference && startVideoMutedPolicy)
    };
}

/**
 * Returns the properties for the "Profile" tab from settings dialog from Redux
 * state.
 *
 * @param {(Function|Object)} stateful -The (whole) redux state, or redux's
 * {@code getState} function to be used to retrieve the state.
 * @returns {Object} - The properties for the "Profile" tab from settings
 * dialog.
 */
export function getProfileTabProps(stateful: IStateful) {
    const state = toState(stateful);
    const {
        authEnabled,
        authLogin,
        conference
    } = state['features/base/conference'];
    const config = state['features/base/config'];
    let { hideEmailInSettings } = config;
    const localParticipant = getLocalParticipant(state);

    if (config.gravatar?.disabled
        || (localParticipant?.avatarURL && localParticipant?.avatarURL.length > 0)) {
        hideEmailInSettings = true;
    }

    return {
        authEnabled: Boolean(conference && authEnabled),
        authLogin,
        displayName: localParticipant?.name,
        email: localParticipant?.email,
        hideEmailInSettings,
        id: localParticipant?.id,
        readOnlyName: isNameReadOnly(state)
    };
}

/**
 * Returns the properties for the "Sounds" tab from settings dialog from Redux
 * state.
 *
 * @param {(Function|Object)} stateful -The (whole) redux state, or redux's
 * {@code getState} function to be used to retrieve the state.
 * @param {boolean} showSoundsSettings - Whether to show the sound settings or not.
 * @returns {Object} - The properties for the "Sounds" tab from settings
 * dialog.
 */
export function getNotificationsTabProps(stateful: IStateful, showSoundsSettings?: boolean) {
    const state = toState(stateful);
    const {
        soundsIncomingMessage,
        soundsParticipantJoined,
        soundsParticipantKnocking,
        soundsParticipantLeft,
        soundsTalkWhileMuted,
        soundsReactions
    } = state['features/base/settings'];

    const enableReactions = isReactionsEnabled(state);
    const moderatorMutedSoundsReactions = state['features/base/conference'].startReactionsMuted ?? false;
    const enabledNotifications = getNotificationsMap(stateful);

    return {
        disabledSounds: state['features/base/config'].disabledSounds || [],
        enabledNotifications,
        showNotificationsSettings: Object.keys(enabledNotifications).length > 0,
        soundsIncomingMessage,
        soundsParticipantJoined,
        soundsParticipantKnocking,
        soundsParticipantLeft,
        soundsTalkWhileMuted,
        soundsReactions,
        enableReactions,
        moderatorMutedSoundsReactions,
        showSoundsSettings
    };
}

/**
 * Returns the visibility state of the audio settings.
 *
 * @param {Object} state - The state of the application.
 * @returns {boolean}
 */
export function getAudioSettingsVisibility(state: IReduxState) {
    return state['features/settings'].audioSettingsVisible;
}

/**
 * Returns the visibility state of the video settings.
 *
 * @param {Object} state - The state of the application.
 * @returns {boolean}
 */
export function getVideoSettingsVisibility(state: IReduxState) {
    return state['features/settings'].videoSettingsVisible;
}
