// #region Imports
import { Signal } from '../../Reactivity/Signal';
/**
 * Abstract base for URL parameter property logic.
 *
 * @private
 */
class UrlParamPropertyBase {
    // #region Fields
    _propertyKey;
    _paramName;
    _toUrl;
    _fromUrl;
    // #endregion
    // #region Ctor
    constructor(propertyKey, config) {
        this._propertyKey = propertyKey;
        this._paramName = config.name ?? propertyKey;
        this._toUrl = config.convert?.toUrl ?? ((value) => String(value));
        this._fromUrl = config.convert?.fromUrl ?? ((value) => {
            try {
                return JSON.parse(value);
            }
            catch {
                return value;
            }
        });
    }
    // #endregion
    // #region Properties
    get paramName() {
        return this._paramName;
    }
    get propertyKey() {
        return this._propertyKey;
    }
    get toUrl() {
        return this._toUrl;
    }
    get fromUrl() {
        return this._fromUrl;
    }
    // #endregion
    // #region Methods
    /**
     * Updates the target object with the value from the URL.
     *
     * @public
     * @param target The target object to update.
     */
    updateFromUrl(target) {
        const hash = window.location.hash.startsWith('#') ? window.location.hash.slice(1) : window.location.hash;
        const [, search] = hash.split('?');
        const urlParams = new URLSearchParams(search || '');
        const rawValue = urlParams.get(this._paramName);
        if (rawValue !== null) {
            const value = this._fromUrl(rawValue);
            if (target[this._propertyKey] !== value) {
                target[this._propertyKey] = value;
            }
        }
    }
    /**
     * Checks if a value exists in the URL.
     *
     * @public
     * @returns `true` if the value exists, otherwise `false`.
     */
    hasValue() {
        const hash = window.location.hash.startsWith('#') ? window.location.hash.slice(1) : window.location.hash;
        const [, search] = hash.split('?');
        const urlParams = new URLSearchParams(search || '');
        return urlParams.has(this._paramName);
    }
}
/**
 * Handles URL parameter property for primitive and object values.
 *
 * @private
 */
class UrlParamValueProperty extends UrlParamPropertyBase {
    // #region Fields
    _value;
    // #endregion
    // #region Ctor
    constructor(config, propertyKey) {
        super(propertyKey, config);
        this._value = config.defaultValue;
    }
    // #endregion
    // #region Methods
    getValue() {
        const hash = window.location.hash.startsWith('#') ? window.location.hash.slice(1) : window.location.hash;
        const [, search] = hash.split('?');
        const urlParams = new URLSearchParams(search || '');
        const rawValue = urlParams.get(this.paramName);
        if (rawValue !== null) {
            return this.fromUrl(rawValue);
        }
        return this._value;
    }
    setValue(newVal) {
        this._value = newVal;
        this.updateUrlParameter(this.paramName, newVal);
    }
    updateUrlParameter(key, value) {
        const hash = window.location.hash.startsWith('#') ? window.location.hash.slice(1) : window.location.hash;
        const [base, search] = hash.split('?');
        const params = new URLSearchParams(search || '');
        params.set(key, this.toUrl(value));
        const newHash = `${base}?${params.toString()}`;
        const url = window.location.origin + window.location.pathname + '#' + newHash;
        window.history.replaceState({}, '', url);
    }
}
/**
 * Handles URL parameter property for Signal values.
 *
 * @private
 */
class UrlParamSignalProperty extends UrlParamPropertyBase {
    // #region Fields
    _signal;
    // #endregion
    // #region Ctor
    constructor(config, propertyKey) {
        super(propertyKey, config);
        this._signal = Signal.from(config.defaultValue);
    }
    // #endregion
    // #region Methods
    getValue() {
        const hash = window.location.hash.startsWith('#') ? window.location.hash.slice(1) : window.location.hash;
        const [, search] = hash.split('?');
        const urlParams = new URLSearchParams(search || '');
        const rawValue = urlParams.get(this.paramName);
        if (rawValue !== null) {
            const value = this.fromUrl(rawValue);
            this._signal.set(value);
        }
        return this._signal;
    }
    setValue(newVal) {
        this._signal = newVal;
        this.updateUrlParameter(this.paramName, newVal.get());
    }
    updateUrlParameter(key, value) {
        const hash = window.location.hash.startsWith('#') ? window.location.hash.slice(1) : window.location.hash;
        const [base, search] = hash.split('?');
        const params = new URLSearchParams(search || '');
        params.set(key, this.toUrl(value));
        const newHash = `${base}?${params.toString()}`;
        const url = window.location.origin + window.location.pathname + '#' + newHash;
        window.history.replaceState({}, '', url);
    }
}
/**
 * Decorator for URL parameter properties.
 *
 * @public
 */
export function UrlParam(config) {
    return (target, propertyKey) => {
        const type = config?.type ?? typeof target[propertyKey];
        let urlParam = null;
        if (type === Signal) {
            urlParam = new UrlParamSignalProperty(config ?? {}, propertyKey);
        }
        else {
            urlParam = new UrlParamValueProperty(config ?? {}, propertyKey);
        }
        const originalDescriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
        window.addEventListener('popstate', () => {
            urlParam.updateFromUrl(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 (urlParam.getValue() !== undefined) {
                    const urlValue = urlParam.getValue();
                    originalSetter?.call(this, urlValue);
                    return urlValue;
                }
                return originalGetter?.call(this);
                // return urlParam.getValue();
            };
            originalDescriptor.set = function set(newVal) {
                urlParam.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: () => urlParam.getValue(),
                set: (newVal) => urlParam.setValue(newVal),
                enumerable: true,
                configurable: true
            });
        }
        return originalDescriptor;
    };
}
//# sourceMappingURL=UrlParamDecorator.js.map