/**
 * Copyright schukai GmbH and contributors 2022. All Rights Reserved.
 * Node module: @schukai/monster
 * This file is licensed under the AGPLv3 License.
 * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
 */

import {internalSymbol,instanceSymbol} from "../../constants.mjs";
import {validateString} from "../../types/validate.mjs";
import {Datasource} from "../datasource.mjs";

export {Storage, storageObjectSymbol}

/**
 * @private
 * @type {symbol}
 */
const storageObjectSymbol = Symbol.for  ('@schukai/monster/data/datasource/storage/@@storageObject')

/**
 * The class represents a record.
 * 
 * @license AGPLv3
 * @since 1.22.0
 * @copyright schukai GmbH
 * @memberOf Monster.Data.Datasource
 * @summary The Storage class encapsulates the access to data objects over WebStorageAPI.
 */
class Storage extends Datasource {

    /**
     *
     * @param {string} key LocalStorage Key
     * @throws {TypeError} value is not a string
     */
    constructor(key) {
        super();
        this.setOption('key', validateString(key));
    }

    /**
     * This method is called by the `instanceof` operator.
     * @returns {symbol}
     * @since 2.1.0
     */
    static get [instanceSymbol]() {
        return Symbol.for("@schukai/monster/data/datasource/storage");
    }    

    /**
     * @property {string} key=undefined LocalStorage Key
     */
    get defaults() {
        return Object.assign({}, super.defaults, {
            key: undefined,
        });
    }

    /**
     * @throws {Error} this method must be implemented by derived classes.
     * @return {external:Storage}
     * @private
     */
    [storageObjectSymbol]() {
        throw new Error("this method must be implemented by derived classes")
    }

    /**
     * @return {Promise}
     * @throws {Error} the options does not contain a valid json definition
     * @throws {TypeError} value is not a object
     * @throws {Error} the data cannot be read
     */
    read() {
        const self = this;

        const storage = self[storageObjectSymbol]();

        return new Promise(function (resolve) {
            const data = JSON.parse(storage.getItem(self.getOption('key')));
            self.set(data??{});
            resolve();
        })

    }

    /**
     * @return {Storage}
     * @throws {Error} the data cannot be written
     */
    write() {
        const self = this;

        const storage = self[storageObjectSymbol]();

        return new Promise(function (resolve) {

            const data = self.get();
            if (data === undefined) {
                storage.removeItem(self.getOption('key'));
            } else {
                storage.setItem(self.getOption('key'), JSON.stringify(data));
            }

            resolve();
        })
    }

    /**
     * @return {Storage}
     */
    getClone() {
        const self=this;
        return new Storage(self[internalSymbol].getRealSubject()['options'].key);
    } 
    
}