// #region Imports
import { query } from '../../Controls/Directives/Query';
import { queryShadow } from '../../Controls/Directives/QueryShadow';
import { prevent, stop } from '@breadstone/mosaik-elements';
/**
 * A method decorator that listens to a specific event on a target element.
 *
 * @param eventName - The name of the event to listen for.
 * @param target - The target element to listen on. Can be `self`, `window`, `document`, or an object with a `target` property for querying.
 *                 If no target is specified, it **defaults** to `self`.
 * @param options - Optional options for the event listener, such as `preventDefault` and `stopPropagation`.
 * @public
 * @example
 * ```typescript
 * class MyElement extends HTMLElement {
 *    @Listen('click', 'self', { preventDefault: true })
 *    handleClick(event: MouseEvent) {
 *        console.log('Element clicked!', event);
 *   }
 *
 *   // or
 *
 *   @Listen('click', { target: '.my-button', shadow: true })
 *   handleClick(event: MouseEvent) {
 *       console.log('Button clicked!', event);
 *   }
 * ```
 *
 *
 */
export const Listen = (eventName, target = 'self', options) => (prototype, propertyKey) => {
    const originalConnectedCallback = prototype.connectedCallback;
    const originalDisconnectedCallback = prototype.disconnectedCallback;
    const originalFirstUpdated = prototype.firstUpdated;
    const createHandler = (instance, methodName) => (event) => {
        let handled = false;
        if (options?.preventDefault) {
            handled = true;
            prevent(event, () => instance[methodName](event));
        }
        if (options?.stopPropagation) {
            handled = true;
            stop(event, () => instance[methodName](event));
        }
        if (!handled) {
            instance[methodName](event);
        }
    };
    if (typeof target !== 'object') {
        prototype.connectedCallback = function () {
            let actualTarget = null;
            if (target === 'self') {
                actualTarget = this;
            }
            else if (target === 'window') {
                actualTarget = window;
            }
            else {
                actualTarget = document;
            }
            const handler = createHandler(this, propertyKey);
            actualTarget.addEventListener(eventName, handler, options);
            if (originalConnectedCallback) {
                originalConnectedCallback.call(this);
            }
            this[`__${String(propertyKey)}_handler`] = handler;
            this[`__${String(propertyKey)}_actualTarget`] = actualTarget;
        };
    }
    else {
        const setupListener = (that) => {
            let actualTarget = null;
            if (target.shadow !== false) {
                actualTarget = queryShadow(that, target.target);
            }
            else {
                actualTarget = query(that, target.target);
            }
            if (!actualTarget) {
                console.warn(`[Listen]: Could not resolve target element for event '${eventName}' with selector '${target.target}'.`);
            }
            const handler = createHandler(that, propertyKey);
            actualTarget?.addEventListener(eventName, handler, options);
            that[`__${String(propertyKey)}_handler`] = handler;
            that[`__${String(propertyKey)}_actualTarget`] = actualTarget;
        };
        if (originalFirstUpdated) {
            prototype.firstUpdated = function () {
                setupListener(this);
                originalFirstUpdated.call(this);
            };
        }
        else {
            prototype.connectedCallback = function () {
                setupListener(this);
                if (originalConnectedCallback) {
                    originalConnectedCallback.call(this);
                }
            };
        }
    }
    prototype.disconnectedCallback = function () {
        const actualTarget = this[`__${String(propertyKey)}_actualTarget`];
        const handler = this[`__${String(propertyKey)}_handler`];
        if (actualTarget && handler) {
            actualTarget.removeEventListener(eventName, handler);
        }
        if (originalDisconnectedCallback) {
            originalDisconnectedCallback.call(this);
        }
    };
};
//# sourceMappingURL=ListenDecorator.js.map