/**
 * @license
 * Copyright Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { getShadowElement, isDeepEqual } from '@tylertech/forge-core';
import { BaseAdapter } from '../../core/base/base-adapter';
import { LIST_CONSTANTS } from '../list/list-constants';
import { LIST_ITEM_CONSTANTS } from './list-item-constants';
import { setDefaultAria } from '../../constants';
export class ListItemAdapter extends BaseAdapter {
    constructor(component) {
        super(component);
        this._slotListener = this._onSlotChange.bind(this);
        this._rootElement = getShadowElement(component, LIST_ITEM_CONSTANTS.selectors.ROOT);
        this._defaultSlotElement = getShadowElement(component, 'slot:not([name])');
        this._startSlotElement = getShadowElement(component, 'slot[name=start]');
        this._endSlotElement = getShadowElement(component, 'slot[name=end]');
    }
    get interactiveElement() {
        return this._interactiveElement;
    }
    initialize() {
        const list = this._getParentList();
        if (list) {
            this._inheritParentListProps(list);
        }
        this._component[setDefaultAria]({ role: 'listitem' }, { setAttribute: !this._component.hasAttribute('role') });
    }
    destroy() {
        this._rootElement.removeEventListener('slotchange', this._slotListener);
        this._tryCleanupObservers();
    }
    initializeInteractiveObserver(listener) {
        this._interactiveStateChangeListener = listener;
        this._rootElement.addEventListener('slotchange', this._slotListener);
        this._initializeInteractiveElement();
    }
    destroyInteractiveObserver() {
        var _a;
        this._rootElement.removeEventListener('slotchange', this._slotListener);
        this._tryCleanupObservers();
        (_a = this._interactiveStateChangeListener) === null || _a === void 0 ? void 0 : _a.call(this, false);
        this._interactiveStateChangeListener = undefined;
    }
    addRootListener(type, listener, options) {
        this._rootElement.addEventListener(type, listener, options);
    }
    removeRootListener(type, listener, options) {
        this._rootElement.removeEventListener(type, listener, options);
    }
    setActive(value) {
        if (this._focusIndicatorElement) {
            this._focusIndicatorElement.active = value;
        }
    }
    /**
     * Attempts to set the selected state of the list item element and it's visual indicators
     * @param value The value to compare to the parent list element's selected value
     * @returns Returns whether the list item is selected, or null otherwise.
     */
    trySelect(value) {
        const list = this._getParentList();
        if ((list === null || list === void 0 ? void 0 : list.selectedValue) === undefined) {
            return null;
        }
        const listValues = Array.isArray(list.selectedValue) ? list.selectedValue : [list.selectedValue];
        const isSelected = listValues.some(v => isDeepEqual(v, value));
        return isSelected;
    }
    animateStateLayer() {
        var _a;
        (_a = this._stateLayerElement) === null || _a === void 0 ? void 0 : _a.playAnimation();
    }
    tempDeactivateFocusIndicator() {
        if (this._focusIndicatorElement) {
            this._focusIndicatorElement.active = false;
        }
    }
    _onSlotChange(evt) {
        // We only care about slot changes in the default slot, start/leading slot, and end/trailing slot to search for interactive elements
        const interactiveSlotNames = ['', 'start', 'end', 'leading', 'trailing'];
        const isInteractiveSlot = interactiveSlotNames.includes(evt.target.name);
        if (!isInteractiveSlot) {
            return;
        }
        this._initializeInteractiveElement();
    }
    _initializeInteractiveElement() {
        this._tryCleanupObservers();
        // We always want to check for form control-like elements first as those take precedence over slotted anchor and button
        // elements as the interactive element.
        const assignedStartElements = this._startSlotElement.assignedElements({ flatten: true });
        const assignedEndElements = this._endSlotElement.assignedElements({ flatten: true });
        const assignedStartEndElements = [...assignedStartElements, ...assignedEndElements];
        const slottedFormControl = assignedStartEndElements.find(e => e.matches(LIST_ITEM_CONSTANTS.selectors.FORM_CONTROL_LIKE));
        if (slottedFormControl) {
            this._attachInteractiveFormControl(slottedFormControl);
            return;
        }
        // If no form control-like elements are found, we check for slotted anchor or button elements within the default slot next.
        // If an anchor element is found, we create an internal anchor element to prevent the list item from being focusable.
        // If a button element is found, we sync the disabled state and attach a mutation observer to watch for changes.
        const assignedElements = this._defaultSlotElement.assignedElements({ flatten: true });
        const slottedAnchor = assignedElements.find(e => e.tagName === 'A');
        const slottedButtonLike = assignedElements.find(e => e.matches(LIST_ITEM_CONSTANTS.selectors.BUTTON_LIKE));
        // Attempt to remove the internal anchor element if it exists before we attach to a different interactive element
        const internalAnchor = getShadowElement(this._component, LIST_ITEM_CONSTANTS.selectors.INTERNAL_ANCHOR);
        if (internalAnchor) {
            internalAnchor.remove();
        }
        // Slotted anchors take precedence over slotted button-like elements
        this._interactiveElement = slottedAnchor !== null && slottedAnchor !== void 0 ? slottedAnchor : slottedButtonLike;
        this._setInteractive(!!this._interactiveElement);
        if (slottedAnchor) {
            this._attachInteractiveAnchor(slottedAnchor);
        }
        else if (slottedButtonLike) {
            this._attachInteractiveButtonLike(slottedButtonLike);
        }
    }
    _attachInteractiveFormControl(element) {
        this._interactiveElement = element;
        this._setInteractive(!!this._interactiveElement);
        if (this._focusIndicatorElement) {
            this._focusIndicatorElement.targetElement = element;
        }
        // Listen for changes to the disabled attribute and aria-disabled attribute to synchronize our disabled state
        this._syncDisabled(element);
        this._disabledAttrObserver = new MutationObserver(() => this._syncDisabled(element));
        this._disabledAttrObserver.observe(element, { attributes: true, attributeFilter: ['disabled', 'aria-disabled'] });
    }
    _attachInteractiveAnchor(element) {
        var _a;
        // Create an internal (facade) anchor element that covers the entire list item to show the href in the status bar
        // but is not focusable or presented to assistive technology. All clicks on the internal anchor are forwarded to the
        // slotted anchor element.
        const internalAnchor = (_a = getShadowElement(this._component, LIST_ITEM_CONSTANTS.selectors.INTERNAL_ANCHOR)) !== null && _a !== void 0 ? _a : document.createElement('a');
        internalAnchor.href = element.href;
        internalAnchor.tabIndex = -1;
        internalAnchor.id = LIST_ITEM_CONSTANTS.ids.INTERNAL_ANCHOR;
        internalAnchor.classList.add(LIST_ITEM_CONSTANTS.classes.INTERNAL_ANCHOR);
        internalAnchor.setAttribute('aria-hidden', 'true');
        this._rootElement.appendChild(internalAnchor);
        if (this._focusIndicatorElement) {
            this._focusIndicatorElement.targetElement = element;
        }
        // Listen for changes to the href attribute to synchronize our internal anchor's href
        this._anchorAttrObserver = new MutationObserver(() => internalAnchor.href = element.href);
        this._anchorAttrObserver.observe(element, { attributes: true, attributeFilter: ['href'] });
    }
    _attachInteractiveButtonLike(element) {
        if (this._focusIndicatorElement) {
            this._focusIndicatorElement.targetElement = element;
        }
        this._syncDisabled(element);
        this._disabledAttrObserver = new MutationObserver(() => this._syncDisabled(element));
        this._disabledAttrObserver.observe(element, { attributes: true, attributeFilter: ['disabled', 'aria-disabled'] });
    }
    _setInteractive(value) {
        var _a, _b, _c;
        this._rootElement.classList.toggle(LIST_ITEM_CONSTANTS.classes.INTERACTIVE, value);
        // Notify ourselves that the interactive state has changed so we can attach/remove event listeners
        (_a = this._interactiveStateChangeListener) === null || _a === void 0 ? void 0 : _a.call(this, value);
        if (value) {
            if (!this._focusIndicatorElement) {
                this._focusIndicatorElement = document.createElement('forge-focus-indicator');
                this._focusIndicatorElement.setAttribute('part', 'focus-indicator');
                this._focusIndicatorElement.inward = true;
                this._rootElement.appendChild(this._focusIndicatorElement);
            }
            if (!this._stateLayerElement) {
                this._stateLayerElement = document.createElement('forge-state-layer');
                this._stateLayerElement.targetElement = this._rootElement;
                this._stateLayerElement.setAttribute('exportparts', 'surface:state-layer');
                this._rootElement.appendChild(this._stateLayerElement);
            }
        }
        else {
            (_b = this._focusIndicatorElement) === null || _b === void 0 ? void 0 : _b.remove();
            this._focusIndicatorElement = undefined;
            (_c = this._stateLayerElement) === null || _c === void 0 ? void 0 : _c.remove();
            this._stateLayerElement = undefined;
        }
    }
    _syncDisabled(element) {
        const isDisabled = element.hasAttribute('disabled') || element.getAttribute('aria-disabled') === 'true';
        this._rootElement.classList.toggle(LIST_ITEM_CONSTANTS.classes.DISABLED, isDisabled);
        this._setInteractive(!isDisabled);
    }
    _getParentList() {
        return this._component.closest(LIST_CONSTANTS.elementName);
    }
    _inheritParentListProps(list) {
        if (list.hasAttribute(LIST_CONSTANTS.attributes.NONINTERACTIVE)) {
            this._component.noninteractive = true;
        }
        if (list.hasAttribute(LIST_CONSTANTS.attributes.DENSE)) {
            this._component.dense = true;
        }
        if (list.hasAttribute(LIST_CONSTANTS.attributes.INDENTED)) {
            this._component.indented = true;
        }
        if (list.hasAttribute(LIST_CONSTANTS.attributes.TWO_LINE)) {
            this._component.twoLine = true;
        }
        if (list.hasAttribute(LIST_CONSTANTS.attributes.THREE_LINE)) {
            this._component.threeLine = true;
        }
    }
    _tryCleanupObservers() {
        var _a, _b;
        (_a = this._disabledAttrObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
        this._disabledAttrObserver = undefined;
        (_b = this._anchorAttrObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
        this._anchorAttrObserver = undefined;
    }
}
