// #region Imports
import { Signal } from '../../Reactivity/Signal';
import { StorageServiceLocator } from '../Storage';
/**
 * Abstract base for persisted property logic.
 *
 * @private
 */
class PersistedPropertyBase {
    // #region Fields
    _propertyKey;
    _storageKey;
    _toStorage;
    _fromStorage;
    // #endregion
    // #region Ctor
    constructor(propertyKey, config) {
        this._propertyKey = propertyKey;
        this._storageKey = config.storageKey ?? propertyKey;
        this._toStorage = config.convert?.toStorage ?? ((value) => value);
        this._fromStorage = config.convert?.fromStorage ?? ((value) => {
            return value;
        });
    }
    // #endregion
    // #region Properties
    get storageKey() {
        return this._storageKey;
    }
    get propertyKey() {
        return this._propertyKey;
    }
    get toStorage() {
        return this._toStorage;
    }
    get fromStorage() {
        return this._fromStorage;
    }
    // #endregion
    // #region Methods
    /**
     * Updates the target object with the value from local storage.
     *
     * @public
     * @param target The target object to update.
     */
    updateFromLocalStorage(target) {
        const rawValue = localStorage.getItem(this._storageKey);
        if (rawValue !== null) {
            const storedValue = this._fromStorage(rawValue);
            if (target[this._propertyKey] !== storedValue) {
                target[this._propertyKey] = storedValue;
            }
        }
    }
    /**
     * Checks if a value exists in storage.
     *
     * @public
     * @returns `true` if the value exists, otherwise `false`.
     */
    hasValue() {
        return StorageServiceLocator.current.has(this._storageKey);
    }
}
/**
 * Handles persisted property for primitive and object values.
 *
 * @private
 */
class PersistedValueProperty extends PersistedPropertyBase {
    // #region Fields
    _value;
    // #endregion
    // #region Ctor
    constructor(config, propertyKey) {
        super(propertyKey, config);
        this._value = config.defaultValue;
    }
    // #endregion
    // #region Methods
    getValue() {
        const rawValue = StorageServiceLocator.current.get(this.storageKey);
        if (rawValue !== undefined && rawValue !== null) {
            return this.fromStorage(typeof rawValue === 'string' ? rawValue : this.toStorage(rawValue));
        }
        return this._value;
    }
    setValue(newVal) {
        this._value = newVal;
        StorageServiceLocator.current.set(this.storageKey, this.toStorage(newVal));
    }
}
/**
 * Handles persisted property for Signal values.
 *
 * @private
 */
class PersistedSignalProperty extends PersistedPropertyBase {
    // #region Fields
    _signal;
    // #endregion
    // #region Ctor
    constructor(config, propertyKey) {
        super(propertyKey, config);
        this._signal = Signal.from(config.defaultValue);
    }
    // #endregion
    // #region Methods
    getValue() {
        const rawValue = StorageServiceLocator.current.get(this.storageKey);
        if (rawValue !== undefined && rawValue !== null) {
            const value = this.fromStorage(typeof rawValue === 'string' ? rawValue : this.toStorage(rawValue));
            this._signal.set(value);
        }
        return this._signal;
    }
    setValue(newVal) {
        this._signal = newVal;
        StorageServiceLocator.current.set(this.storageKey, this.toStorage(newVal.get()));
    }
}
/**
 * Decorator for persisted properties.
 *
 * @public
 */
export function Persisted(config) {
    return (target, propertyKey) => {
        const type = config?.type ?? typeof target[propertyKey];
        let persisted = null;
        if (type === Signal) {
            persisted = new PersistedSignalProperty(config ?? {}, propertyKey);
        }
        else {
            persisted = new PersistedValueProperty(config ?? {}, propertyKey);
        }
        const originalDescriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
        window.addEventListener('storage', (event) => {
            if (event.key === persisted.storageKey) {
                persisted.updateFromLocalStorage(target);
            }
        });
        if (originalDescriptor && (originalDescriptor.get ?? originalDescriptor.set)) {
            // eslint-disable-next-line @typescript-eslint/unbound-method
            const originalGetter = originalDescriptor.get;
            // eslint-disable-next-line @typescript-eslint/unbound-method
            const originalSetter = originalDescriptor.set;
            originalDescriptor.get = function get() {
                if (StorageServiceLocator.current.has(persisted.storageKey)) {
                    const storageValue = persisted.getValue();
                    // set the value to the original property
                    // important: if the property is modified in the setter, this has no effect here
                    originalSetter?.call(this, storageValue);
                    return storageValue;
                }
                return originalGetter?.call(this);
                // return persisted.getValue();
            };
            originalDescriptor.set = function set(newVal) {
                persisted.setValue(newVal);
                originalSetter?.call(this, newVal);
            };
            // Object.defineProperty(target, propertyKey, originalDescriptor);
        }
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore
        // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
        if (delete target[propertyKey]) {
            Object.defineProperty(target, propertyKey, {
                get: () => persisted.getValue(),
                set: (newVal) => persisted.setValue(newVal),
                enumerable: true,
                configurable: true
            });
        }
        return originalDescriptor;
    };
}
//# sourceMappingURL=PersistedDecorator.js.map