import Toastify from 'toastify-js';
// import { t } from './utils/l10n.js';
import './toast.css';

/**
 * Enum of available Toast types
 */
export enum ToastType {
    ERROR = 'toast-error',
    WARNING = 'toast-warning',
    INFO = 'toast-info',
    SUCCESS = 'toast-success',
    PERMANENT = 'toast-error',
    UNDO = 'toast-undo',
}

/** @deprecated Use ToastAriaLive.OFF */
export const TOAST_ARIA_LIVE_OFF = 'off';
/** @deprecated Use ToastAriaLive.POLITE */
export const TOAST_ARIA_LIVE_POLITE = 'polite';
/** @deprecated Use ToastAriaLive.ASSERTIVE */
export const TOAST_ARIA_LIVE_ASSERTIVE = 'assertive';

export enum ToastAriaLive {
    OFF = TOAST_ARIA_LIVE_OFF,
    POLITE = TOAST_ARIA_LIVE_POLITE,
    ASSERTIVE = TOAST_ARIA_LIVE_ASSERTIVE,
}

/** Timeout in ms of a undo toast */
export const TOAST_UNDO_TIMEOUT = 10000;
/** Default timeout in ms of toasts */
export const TOAST_DEFAULT_TIMEOUT = 4000;
/** Timeout value to show a toast permanently */
export const TOAST_PERMANENT_TIMEOUT = -1;

/**
 * Type of a toast
 * @see https://apvarun.github.io/toastify-js/
 * @notExported
 */
type Toast = ReturnType<typeof Toastify>;

export interface ToastOptions {
    /**
     * Defines the timeout in milliseconds after which the toast is closed. Set to -1 to have a persistent toast.
     */
    timeout?: number;

    /**
     * Set to true to allow HTML content inside of the toast text
     * @default false
     */
    isHTML?: boolean;

    /**
     * Set a type of {ToastType} to style the modal
     */
    type?: ToastType;

    /**
     * Provide a function that is called after the toast is removed
     */
    onRemove?: () => void;

    /**
     * Provide a function that is called when the toast is clicked
     */
    onClick?: () => void;

    /**
     * Make the toast closable
     */
    close?: boolean;

    /**
     * Specify the element to attach the toast element to (for testing)
     */
    selector?: string;

    /**
     * Whether the messages should be announced to screen readers.
     * See the following docs for an explanation when to use which:
     * https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions
     *
     * By default, errors are announced assertive and other messages "polite".
     */
    ariaLive?: ToastAriaLive;
}

/**
 * Show a toast message
 *
 * @param data Message to be shown in the toast, any HTML is removed by default
 * @param options
 */
export function showMessage(data: string | Node, options?: ToastOptions): Toast {
    options = Object.assign(
        {
            timeout: TOAST_DEFAULT_TIMEOUT,
            isHTML: false,
            type: undefined,
            // An undefined selector defaults to the body element
            selector: undefined,
            onRemove: () => {},
            onClick: undefined,
            close: true,
        },
        options,
    );

    if (typeof data === 'string' && !options.isHTML) {
        // fime mae sure that text is extracted
        const element = document.createElement('div');
        element.innerHTML = data;
        data = element.innerText;
    }
    let classes = options.type ?? '';

    if (typeof options.onClick === 'function') {
        classes += ' toast-with-click ';
    }

    const isNode = data instanceof Node;

    let ariaLive: ToastAriaLive = ToastAriaLive.POLITE;
    if (options.ariaLive) {
        ariaLive = options.ariaLive;
    } else if (options.type === ToastType.ERROR || options.type === ToastType.UNDO) {
        ariaLive = ToastAriaLive.ASSERTIVE;
    }

    const toast = Toastify({
        [!isNode ? 'text' : 'node']: data,
        duration: options.timeout,
        callback: options.onRemove,
        onClick: options.onClick,
        close: options.close,
        gravity: 'top',
        selector: options.selector,
        position: 'right',
        backgroundColor: '',
        className: 'dialogs ' + classes,
        escapeMarkup: !options.isHTML,
        ariaLive,
    });

    toast.showToast();

    return toast;
}

/**
 * Show a toast message with error styling
 *
 * @param text Message to be shown in the toast, any HTML is removed by default
 * @param options
 */
export function showError(text: string, options?: ToastOptions): Toast {
	return showMessage(text, { ...options, type: ToastType.ERROR })
}

export default {
    updatableNotification: null,

    getDefaultNotificationFunction: null,

    /**
     * Shows a notification that disappears after x seconds, default is
     * 7 seconds
     *
     * @param {string} text Message to show
     * @param {Array} [options] options array
     * @param {number} [options.timeout=7] timeout in seconds, if this is 0 it will show the message permanently
     * @param {boolean} [options.isHTML=false] an indicator for HTML notifications (true) or text (false)
     * @param {string} [options.type] notification type
     * @return {JQuery} the toast element
     */
    showTemporary(text, options = { timeout: 3000 }) {
        options = options || {};
        options.timeout = options.timeout || TOAST_DEFAULT_TIMEOUT;
        const toast = showMessage(text, options);
        toast.toastElement.toastify = toast;
        // return $(toast.toastElement)
    },

    /**
     * Show a toast message with error styling
     *
     * @param text Message to be shown in the toast, any HTML is removed by default
     * @param options
     */
    showError(text: string, options?: ToastOptions): Toast {
        return showMessage(text, { ...options, type: ToastType.ERROR });
    },

    /**
     * Show a toast message with warning styling
     *
     * @param text Message to be shown in the toast, any HTML is removed by default
     * @param options
     */
    showWarning(text: string, options?: ToastOptions): Toast {
        return showMessage(text, { ...options, type: ToastType.WARNING });
    },

    /**
     * Show a toast message with info styling
     *
     * @param text Message to be shown in the toast, any HTML is removed by default
     * @param options
     */
    showInfo(text: string, options?: ToastOptions): Toast {
        return showMessage(text, { ...options, type: ToastType.INFO });
    },

    /**
     * Show a toast message with success styling
     *
     * @param text Message to be shown in the toast, any HTML is removed by default
     * @param options
     */
    showSuccess(text: string, options?: ToastOptions): Toast {
        return showMessage(text, { ...options, type: ToastType.SUCCESS });
    },
};
