import { getAvailableStorage } from './storage.js';
export const STORE_KEY = '__coveo.analytics.history';
export const MAX_NUMBER_OF_HISTORY_ELEMENTS = 20;
export const MIN_THRESHOLD_FOR_DUPLICATE_VALUE = 1000 * 60;
export const MAX_VALUE_SIZE = 75;
export class HistoryStore {
    static instance = null;
    static getInstance(store) {
        if (!HistoryStore.instance) {
            HistoryStore.instance = new HistoryStore(store);
        }
        return HistoryStore.instance;
    }
    store;
    constructor(store) {
        this.store = store || getAvailableStorage();
    }
    /**
     * @deprecated Synchronous method is deprecated, use addElementAsync instead. This method will NOT work with react-native.
     */
    addElement(elem) {
        elem.internalTime = new Date().getTime();
        elem = this.cropQueryElement(this.stripEmptyQuery(elem));
        const currentHistory = this.getHistoryWithInternalTime();
        if (currentHistory !== null) {
            if (this.isValidEntry(elem)) {
                this.setHistory([elem].concat(currentHistory));
            }
        }
        else {
            this.setHistory([elem]);
        }
    }
    async addElementAsync(elem) {
        elem.internalTime = new Date().getTime();
        elem = this.cropQueryElement(this.stripEmptyQuery(elem));
        const currentHistory = await this.getHistoryWithInternalTimeAsync();
        if (currentHistory !== null) {
            if (this.isValidEntry(elem)) {
                this.setHistory([elem].concat(currentHistory));
            }
        }
        else {
            this.setHistory([elem]);
        }
    }
    /**
     * @deprecated Synchronous method is deprecated, use getHistoryAsync instead. This method will NOT work with react-native.
     */
    getHistory() {
        const history = this.getHistoryWithInternalTime();
        return this.stripEmptyQueries(this.stripInternalTime(history));
    }
    async getHistoryAsync() {
        const history = await this.getHistoryWithInternalTimeAsync();
        return this.stripEmptyQueries(this.stripInternalTime(history));
    }
    getHistoryWithInternalTime() {
        try {
            const elements = this.store.getItem(STORE_KEY);
            if (elements && typeof elements === 'string') {
                return JSON.parse(elements);
            }
            else {
                return [];
            }
        }
        catch (e) {
            // When using the Storage APIs (localStorage/sessionStorage)
            // Safari says that those APIs are available but throws when making
            // a call to them.
            return [];
        }
    }
    async getHistoryWithInternalTimeAsync() {
        try {
            const elements = await this.store.getItem(STORE_KEY);
            if (elements) {
                return JSON.parse(elements);
            }
            else {
                return [];
            }
        }
        catch (e) {
            // When using the Storage APIs (localStorage/sessionStorage)
            // Safari says that those APIs are available but throws when making
            // a call to them.
            return [];
        }
    }
    setHistory(history) {
        try {
            this.store.setItem(STORE_KEY, JSON.stringify(history.slice(0, MAX_NUMBER_OF_HISTORY_ELEMENTS)));
        }
        catch (e) {
            /* refer to this.getHistory() */
        }
    }
    clear() {
        try {
            this.store.removeItem(STORE_KEY);
        }
        catch (e) {
            /* refer to this.getHistory() */
        }
    }
    getMostRecentElement() {
        const currentHistory = this.getHistoryWithInternalTime();
        if (Array.isArray(currentHistory)) {
            const sorted = currentHistory.sort((first, second) => {
                // Internal time might not be set for all history element (on upgrade).
                // Ensure to return the most recent element for which we have a value for internalTime.
                return (second.internalTime || 0) - (first.internalTime || 0);
            });
            return sorted[0];
        }
        return null;
    }
    cropQueryElement(part) {
        if (part.name && part.value && part.name.toLowerCase() === 'query') {
            part.value = part.value.slice(0, MAX_VALUE_SIZE);
        }
        return part;
    }
    isValidEntry(elem) {
        const lastEntry = this.getMostRecentElement();
        if (lastEntry && lastEntry.value === elem.value) {
            return ((elem.internalTime || 0) - (lastEntry.internalTime || 0) >
                MIN_THRESHOLD_FOR_DUPLICATE_VALUE);
        }
        return true;
    }
    stripInternalTime(history) {
        if (Array.isArray(history)) {
            return history.map((part) => {
                const { name, time, value } = part;
                return { name, time, value };
            });
        }
        return [];
    }
    stripEmptyQuery(part) {
        const { name, time, value } = part;
        if (name &&
            typeof value === 'string' &&
            name.toLowerCase() === 'query' &&
            value.trim() === '') {
            return { name, time };
        }
        return part;
    }
    stripEmptyQueries(history) {
        return history.map((part) => this.stripEmptyQuery(part));
    }
}
export default HistoryStore;
