// #region Imports
import { RendererServiceLocator } from '../../../../Dom/Renderer';
import { PureEventEmitter } from '@breadstone/mosaik-elements';
export class Helmet {
    // #region Fields
    static _document = document;
    _defaults;
    _changes;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the `Helmet` class.
     *
     * @public
     */
    constructor() {
        this._defaults = new Map();
        this._changes = new PureEventEmitter();
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `changes` property.
     *
     * @public
     * @readonly
     */
    get changes() {
        return this._changes;
    }
    // #endregion
    // #region Methods
    /**
     * @public
     */
    defaults(tag, data) {
        this._defaults.set(tag, new Map());
        for (const [key, value] of Object.entries(data)) {
            this._defaults.get(tag)?.set(key, value);
            switch (tag) {
                case 'title': {
                    const existingElement = Array.from(document.head.querySelectorAll(tag)).at(0);
                    if (!existingElement) {
                        const element = this.createElement(tag, (e) => {
                            e.text = value;
                            e.setAttribute('data-helmet-default', '');
                        });
                        Helmet._document.head.appendChild(element);
                    }
                    else {
                        console.warn(`[Helmet]: The tag ${tag} already exists.`);
                        existingElement.text = value;
                        existingElement.setAttribute('data-helmet-default', '');
                    }
                    break;
                }
                case 'meta': {
                    const existingElement = Array.from(document.head.querySelectorAll(`${tag}[name="${key}"]`)).at(0);
                    if (!existingElement) {
                        const element = this.createElement(tag, (e) => {
                            e.name = key;
                            e.content = value;
                            e.setAttribute('data-helmet-default', '');
                        });
                        Helmet._document.head.appendChild(element);
                    }
                    else {
                        console.warn(`[Helmet]: The tag ${tag} already exists.`);
                        existingElement.name = key;
                        existingElement.content = value;
                        existingElement.setAttribute('data-helmet-default', '');
                    }
                    break;
                }
                default:
                    throw new Error('Not implemented');
            }
        }
        this._changes.emit({
            tag: tag,
            entries: data
        });
    }
    set(tag, ...args) {
        switch (tag) {
            case 'title': {
                if (args[0] instanceof HTMLTitleElement) {
                    if (!args[0].hasAttribute('data-helmet')) {
                        const newElement = this.cloneElement(args[0]);
                        Helmet._document.head.querySelectorAll(`${tag}[data-helmet]`).forEach((el) => el.remove());
                        Helmet._document.head.appendChild(newElement);
                        // remove the existing default tag
                        if (this.hasDefault(tag, '')) {
                            this.removeDefault(tag, '');
                        }
                        return newElement;
                    }
                    return args[0];
                }
                else if (typeof args[0] === 'string' && typeof args[1] === 'string') {
                    const existingElement = Helmet._document.head.querySelector('title') ??
                        RendererServiceLocator.current.createElement('title');
                    if (!existingElement) {
                        const newElement = this.createElement(tag, (e) => {
                            e.text = args[1];
                        });
                        Helmet._document.head.querySelectorAll(`${tag}[data-helmet]`).forEach((el) => el.remove());
                        Helmet._document.head.appendChild(newElement);
                        return newElement;
                    }
                    console.warn(`[Helmet]: The tag ${tag} already exists.`);
                    existingElement.text = args[1];
                    this.setAttribute(existingElement);
                    return existingElement;
                }
                throw new Error('Invalid arguments');
            }
            case 'meta': {
                if (args[0] instanceof HTMLMetaElement) {
                    if (!args[0].hasAttribute('data-helmet')) {
                        const newElement = this.cloneElement(args[0]);
                        Helmet._document.head.querySelectorAll(`${tag}[name="${newElement.name}"][data-helmet]`).forEach((el) => el.remove());
                        document.head.appendChild(newElement);
                        // remove the existing default tag
                        if (this.hasDefault(tag, newElement.name)) {
                            this.removeDefault(tag, newElement.name);
                        }
                        return newElement;
                    }
                    return args[0];
                }
                else if (typeof args[0] === 'string' && typeof args[1] === 'string') {
                    const existingElement = Helmet._document.head.querySelector(`meta[name="${args[0]}"]`) ??
                        RendererServiceLocator.current.createElement('meta');
                    if (!existingElement) {
                        const newElement = this.createElement(tag, (e) => {
                            e.name = args[0];
                            e.content = args[1];
                        });
                        Helmet._document.head.querySelectorAll(`${tag}[name="${args[0]}"][data-helmet]`).forEach((el) => el.remove());
                        Helmet._document.head.appendChild(newElement);
                        return newElement;
                    }
                    console.warn(`[Helmet]: The tag ${tag} already exists.`);
                    existingElement.name = args[0];
                    existingElement.content = args[1];
                    this.setAttribute(existingElement);
                    return existingElement;
                }
                throw new Error('Invalid arguments');
            }
            default:
                throw new Error('Not implemented');
        }
    }
    remove(tag, ...args) {
        switch (tag) {
            case 'title': {
                if (args[0] instanceof HTMLTitleElement) {
                    const hasDefaultAttribute = args[0].hasAttribute('data-helmet-default');
                    const hasAttribute = args[0].hasAttribute('data-helmet');
                    if (hasAttribute) {
                        if (hasDefaultAttribute) {
                            args[0].removeAttribute('data-helmet');
                            args[0].text = this._defaults.get(tag)?.get('') ?? '';
                        }
                        else {
                            args[0].remove();
                        }
                    }
                    // try restore default
                    this.restore(tag, '');
                }
                else if (typeof args[0] === 'string' && typeof args[1] === 'string') {
                    throw new Error('Not implemented');
                }
                break;
            }
            case 'meta': {
                if (args[0] instanceof HTMLMetaElement) {
                    const hasDefaultAttribute = args[0].hasAttribute('data-helmet-default');
                    const hasAttribute = args[0].hasAttribute('data-helmet');
                    if (hasAttribute) {
                        if (hasDefaultAttribute) {
                            args[0].removeAttribute('data-helmet');
                            args[0].content = this._defaults.get(tag)?.get(args[0].name) ?? '';
                        }
                        else {
                            args[0].remove();
                        }
                    }
                    // try restore default
                    this.restore(tag, args[0].name);
                }
                else if (typeof args[0] === 'string' && typeof args[1] === 'string') {
                    throw new Error('Not implemented');
                }
                break;
            }
            default:
                throw new Error('Not implemented');
        }
    }
    /**
     * @private
     */
    createElement(tagName, factory) {
        const el = Helmet._document.createElement(tagName);
        if (factory) {
            factory(el);
        }
        return el;
    }
    /**
     * @private
     */
    cloneElement(element) {
        const el = element.cloneNode(true);
        el.setAttribute('data-helmet', '');
        return el;
    }
    /**
     * @private
     */
    setAttribute(element) {
        element.setAttribute('data-helmet', crypto.randomUUID());
    }
    /**
     * @private
     */
    restore(tagName, key) {
        const value = this._defaults.get(tagName)?.get(key);
        if (value) {
            switch (tagName) {
                case 'title': {
                    const existingElement = Helmet._document.head.querySelector(`${tagName}[data-helmet-default]`);
                    if (existingElement) {
                        existingElement.text = value;
                        return;
                    }
                    const element = this.createElement('title', (e) => {
                        e.text = value;
                        e.setAttribute('data-helmet-default', '');
                    });
                    Helmet._document.head.appendChild(element);
                    break;
                }
                case 'meta': {
                    const existingElement = Helmet._document.head.querySelector(`${tagName}[data-helmet-default][name="${key}"]`);
                    if (existingElement) {
                        existingElement.content = value;
                        return;
                    }
                    const element = this.createElement('meta', (e) => {
                        e.name = key;
                        e.content = value;
                        e.setAttribute('data-helmet-default', '');
                    });
                    Helmet._document.head.appendChild(element);
                    break;
                }
                default:
                    throw new Error('Not implemented');
            }
        }
    }
    /**
     * @private
     */
    hasDefault(tagName, key) {
        switch (tagName) {
            case 'title': {
                return !!Helmet._document.head.querySelector(`${tagName}[data-helmet-default]`);
            }
            case 'meta': {
                return !!Helmet._document.head.querySelector(`${tagName}[data-helmet-default][name="${key}"]`);
            }
            default:
                throw new Error('Not implemented');
        }
    }
    /**
     * @private
     */
    removeDefault(tagName, key) {
        switch (tagName) {
            case 'title': {
                Helmet._document.head.querySelector(`${tagName}[data-helmet-default]`)?.remove();
                break;
            }
            case 'meta': {
                Helmet._document.head.querySelector(`${tagName}[data-helmet-default][name="${key}"]`)?.remove();
                break;
            }
            default:
                throw new Error('Not implemented');
        }
    }
}
export class HelmetServiceLocator {
    // #region Fields
    static _current;
    // #endregion
    // #region Properties
    static get current() {
        if (!HelmetServiceLocator.isSet()) {
            throw new Error('\'Helmet\' not set.');
        }
        return HelmetServiceLocator._current;
    }
    // #endregion
    // #region Methods
    static isSet() {
        return !!HelmetServiceLocator._current;
    }
    static set(current) {
        HelmetServiceLocator._current = current;
    }
}
HelmetServiceLocator.set(new Helmet());
//# sourceMappingURL=Helmet.js.map