import { cryptoUtil } from '../util/CryptoUtil';
export const setCvStorageApi = storageApi => {
    ClientStorage.setStorageApi(storageApi);
};
class ClientStorage {
    static setStorageApi(storageApi) {
        if (storageApi) {
            if (storageApi.getAllKeys) {
                // Assume AsyncStorage
                ClientStorage._storage = new AsyncStorageAdapter(storageApi);
            }
            else {
                // Assume window.localStorage
                ClientStorage._storage = new LocalStorageAdapter(storageApi);
            }
        }
    }
    constructor() {
        // Attempt to set the default storage backend to localStorage
        if (!ClientStorage._storage) {
            if (typeof (localStorage) !== 'undefined') {
                if (localStorage && localStorage.getItem) {
                    ClientStorage.setStorageApi(localStorage);
                }
            }
        }
    }
    getSecureInstance() {
        return new SecureStorageAdapter(this);
    }
    getItem(key) {
        return ClientStorage._storage.getItem(key);
    }
    getJson(key) {
        return ClientStorage._storage.getJson(key);
    }
    setItem(key, value) {
        return ClientStorage._storage.setItem(key, value);
    }
    setJson(key, value) {
        return ClientStorage._storage.setJson(key, value);
    }
    removeItem(key) {
        return ClientStorage._storage.removeItem(key);
    }
    clearAll() {
        return ClientStorage._storage.clearAll();
    }
    getAllKeys() {
        return ClientStorage._storage.getAllKeys();
    }
    multiRemove(keys) {
        return ClientStorage._storage.multiRemove(keys);
    }
}
class SecureStorageAdapter {
    constructor(storage) {
        this.storage = storage;
        this._secretKey = null;
    }
    set secretKey(secretKey) {
        this._secretKey = secretKey;
    }
    destroySecretKey() {
        delete (this._secretKey);
    }
    getItem(key) {
        return this.storage.getItem(key).then(encValue => {
            if (!encValue)
                return null;
            return cryptoUtil.decrypt(encValue, this.getSecretKey());
        });
    }
    getJson(key) {
        return this.getItem(key).then(JSON.parse);
    }
    setItem(key, value) {
        const encValue = value ? cryptoUtil.encrypt(value, this.getSecretKey()) : null;
        return this.storage.setItem(key, encValue);
    }
    setJson(key, value) {
        let stringVal = null;
        try {
            stringVal = JSON.stringify(value);
        }
        catch (err) {
            return Promise.reject(new Error(`Storage::setItem(${key}) failed to stringify JSON value`));
        }
        return this.setItem(key, stringVal);
    }
    removeItem(key) {
        return this.storage.removeItem(key);
    }
    clearAll() {
        return this.storage.clearAll();
    }
    getAllKeys() {
        return this.storage.getAllKeys();
    }
    multiRemove(keys) {
        return this.storage.multiRemove(keys);
    }
    getSecretKey() {
        if (this._secretKey)
            return this._secretKey;
        throw new Error(`Storage::getSecretKey No secret key set in SecureStorageAdapter`);
    }
}
class AsyncStorageAdapter {
    constructor(api) {
        this.api = api;
    }
    getItem(key) {
        return this.api.getItem(key);
    }
    getJson(key) {
        return this.api.getItem(key).then(value => {
            try {
                return JSON.parse(value);
            }
            catch (err) {
                throw new Error(`Storage::getItem('${key}') parse JSON failed for: ${value}`);
            }
        });
    }
    setItem(key, value) {
        return this.api.setItem(key, value);
    }
    setJson(key, value) {
        let stringVal = null;
        try {
            stringVal = JSON.stringify(value);
        }
        catch (err) {
            return Promise.reject(new Error(`Storage::setItem(${key}) failed to stringify JSON value`));
        }
        return this.api.setItem(key, stringVal);
    }
    removeItem(key) {
        return this.api.removeItem(key);
    }
    clearAll() {
        return this.api.clear();
    }
    getAllKeys() {
        return this.api.getAllKeys();
    }
    multiRemove(keys) {
        return this.api.multiRemove(keys);
    }
}
class LocalStorageAdapter {
    constructor(api) {
        this.api = api;
    }
    getItem(key) {
        return new Promise((resolve, reject) => {
            try {
                const value = this.api.getItem(key);
                resolve(value);
            }
            catch (e) {
                reject(e);
            }
        });
    }
    getJson(key) {
        return new Promise((resolve, reject) => {
            try {
                resolve(JSON.parse(this.api.getItem(key)));
            }
            catch (err) {
                reject(Error(`Storage::getItem('${key}') parse JSON failed`));
            }
        });
    }
    setItem(key, value) {
        return new Promise((resolve, reject) => {
            try {
                resolve(this.api.setItem(key, value));
            }
            catch (e) {
                reject(e);
            }
        });
    }
    setJson(key, value) {
        return new Promise((resolve, reject) => {
            try {
                const stringVal = JSON.stringify(value);
                resolve(this.api.setItem(key, stringVal));
            }
            catch (e) {
                reject(e);
            }
        });
    }
    removeItem(key) {
        return new Promise((resolve, reject) => {
            try {
                resolve(this.api.removeItem(key));
            }
            catch (e) {
                reject(e);
            }
        });
    }
    clearAll() {
        return new Promise((resolve, reject) => {
            try {
                resolve(this.api.clear());
            }
            catch (e) {
                reject(e);
            }
        });
    }
    getAllKeys() {
        return new Promise((resolve, reject) => {
            try {
                const keys = [];
                for (let i = 0; i < this.api.length; i++) {
                    keys.push(this.api.key(i));
                }
                resolve(keys);
            }
            catch (e) {
                reject(e);
            }
        });
    }
    multiRemove(keys) {
        return new Promise((resolve, reject) => {
            try {
                keys.forEach((key) => {
                    this.api.removeItem(key);
                });
                resolve();
            }
            catch (e) {
                reject(e);
            }
        });
    }
}
export const storage = new ClientStorage();
