import { DomNodeSubjectObserverFactory } from '../../../helpers/change-manager/subject/dom-node-subject/dom-node-subject-observer-factory';
import { VegaClick, VegaDropdownClick } from '../../../helpers/event-manager/event-id/vega-event-id';
import { VegaInternalIconManager } from '../../../helpers/icon/internal-icon-manager';
import rteExtensionContextManager from '../helpers/extension-context-manager';
import { isNonNullable } from '../../../types/type-guard';
import { vegaNonceManager } from '../../../helpers/csp/vega-nonce-manager';
/**
 * Base class for rendering extension toolbar button in the rich text editor.
 * The renderer provides common functionalities for rendering toolbar buttons, including:
 * - Rendering a button with an icon, label, and optional arrow icon.
 * - Rendering a dropdown with a list of items.
 * - Rendering dropdown items.
 * - Rendering template for dropdown list.
 * - Determining if the button should be disabled based on the editor context.
 * - Handling button and dropdown click events.
 * Override the methods to customize the rendering and behavior of the button.
 */
export class VegaRTEToolbarButtonRenderer {
  /**
   * Checks if the button should be hidden.
   *
   * @param {VegaRTEToolbarRenderContext} editorContext The render context for the editor.
   * @returns {boolean} True if the button is hidden, false otherwise.
   */
  isHidden(editorContext) {
    return editorContext.sourceEditMode;
  }
  /**
   * Get the extension context for the given rich text editor host element.
   *
   * @typedef HTMLVegaRichTextEditorElement - The type definition for the rich text editor host element.
   * @param {HTMLVegaRichTextEditorElement} el - The host rich text editor element.
   * @returns {Nullable<RTEExtensionContext>} - The extension context associated with the host.
   */
  getExtensionContext(el) {
    return rteExtensionContextManager.getExtensionContext(el);
  }
  /**
   * Renders a dropdown component with the specified properties and children.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {VegaRTEToolbarRenderContext} editorContext - The render context for the editor.
   * @param {RTEToolbarDropdownProps} dropdownProps - The properties for the dropdown.
   * @param {VegaRTERenderResult[]} children - The children elements for the dropdown.
   * @returns {VegaRTERenderResult} - The render result for the dropdown.
   */
  renderDropdown(h, editorContext, dropdownProps, children) {
    return h('vega-dropdown', Object.assign({ trigger: 'click', searchable: false, maxWidth: '200', itemDisplayRule: 'ellipsis', 
      // eslint-disable-next-line jsdoc/require-jsdoc
      ref: (el) => {
        DomNodeSubjectObserverFactory.addUniqueObserverToNode(el, VegaDropdownClick, (input) => {
          const payload = input;
          this.handleDropdownClick(payload.detail, editorContext);
        });
      } }, dropdownProps), children);
  }
  /**
   * Renders the dropdown list template.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {(RTEDropdownListItem | RTEDropdownListGroup)[]} itemList - The list of dropdown items.
   * @returns {VegaRTERenderResult} - The render result for the dropdown list.
   */
  renderDropdownListTemplate(h, itemList) {
    return h('div', {
      slot: 'items',
      onMouseDown: this.preventDefault,
      class: 'rte-dropdown-list',
    }, [
      h('style', { nonce: vegaNonceManager.nonce }, '.rte-dropdown-list vega-dropdown-group vega-dropdown-group { padding-left: 4px }'),
      ...itemList.map((item) => {
        return 'children' in item
          ? this.renderDropdownGroup(h, item)
          : this.renderDropdownItem(h, item);
      }),
    ]);
  }
  /**
   * Renders a dropdown item for the toolbar button.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {RTEDropdownListItem} item - The dropdown item.
   * @returns {VegaRTERenderResult} - The render result for the dropdown item.
   */
  renderDropdownItem(h, item) {
    return h('vega-dropdown-item', {
      key: item.key,
      itemKey: item.key,
      label: item.label,
      prefixIcon: item.icon,
    });
  }
  /**
   * Renders a dropdown item for the toolbar button.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {RTEDropdownListGroup} item - The dropdown group.
   * @returns {VegaRTERenderResult} - The render result for the dropdown item.
   */
  renderDropdownGroup(h, item) {
    return h('vega-dropdown-group', {
      key: item.key,
      label: item.label,
      divide: false,
    }, item.children.map((child) => {
      return 'children' in child
        ? this.renderDropdownGroup(h, child)
        : this.renderDropdownItem(h, child);
    }));
  }
  /**
   * Renders a common toolbar button with optional icon, label, and arrow icon.
   * When a tooltip is provided via `buttonProps.tooltip`, the button is wrapped
   * in a `vega-tooltip` element, matching the behavior of default toolbar buttons.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {VegaRTEToolbarRenderContext} editorContext - The render context for the editor.
   * @param {RTEToolbarButtonProps} buttonProps - The properties for the button.
   * @returns {VegaRTERenderResult} - The render result for the button.
   */
  renderButton(h, editorContext, buttonProps) {
    const { icon, label, showArrowIcon, tooltip } = buttonProps;
    const isDisabled = this.isDisabled(editorContext);
    const button = h('vega-rich-text-editor-toolbar-button', {
      disabled: isDisabled,
      icon: icon,
      hidden: this.isHidden(editorContext),
      selected: this.isSelected(editorContext),
      // eslint-disable-next-line jsdoc/require-jsdoc
      ref: (el) => {
        DomNodeSubjectObserverFactory.addUniqueObserverToNode(el, VegaClick, () => this.handleButtonClick(editorContext));
      },
      onMouseDown: this.preventDefault,
    }, [
      this.renderButtonLabel(h, editorContext, label),
      showArrowIcon
        ? h('vega-icon', {
          size: 'size-8',
          color: isDisabled ? 'text-disabled' : 'text-secondary',
          style: { marginLeft: '8px' },
          icon: VegaInternalIconManager.getIconKey('chevron-down'),
        })
        : null,
    ].filter(isNonNullable));
    if (tooltip === null || tooltip === void 0 ? void 0 : tooltip.text) {
      return h('vega-tooltip', { text: tooltip.text, placement: ['bottom', 'top'] }, [button]);
    }
    return button;
  }
  /**
   * Renders the button label.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {VegaRTEToolbarRenderContext} editorContext - The render context for the editor.
   * @param {Nullable<string>} label - The button label if exist.
   * @returns {Nullable<VegaRTERenderResult>} - The render result for the button.
   */
  renderButtonLabel(h, editorContext, label) {
    if (label) {
      const isDisabled = this.isDisabled(editorContext);
      return h('vega-font', {
        variant: 'font-field-label-sm',
        color: isDisabled ? 'text-disabled' : 'text-primary',
      }, label);
    }
    return null;
  }
  /**
   * Determines if the button should be disabled based on the editor context.
   *
   * @param {VegaRTEToolbarRenderContext} editorContext - The render context for the editor.
   * @returns {boolean} - True if the button should be disabled, false otherwise.
   */
  isDisabled(editorContext) {
    return editorContext.host.disabled;
  }
  /**
   * Checks if the button should be selected.
   *
   * @param {VegaRTEToolbarRenderContext} _editorContext The render context for the editor.
   * @returns {boolean} True if the button is selected, false otherwise.
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  isSelected(_editorContext) {
    return false;
  }
  /**
   * Prevents the default action of the click event. This is to avoid losing focus from the editor when the button is clicked.
   *
   * @param {MouseEvent} e The mouse event to prevent default action for.
   */
  preventDefault(e) {
    e.preventDefault();
  }
  /**
   * Recursively searches for a dropdown list item by its key.
   *
   * @param {(RTEDropdownListItem | RTEDropdownListGroup)[]} itemList The list of dropdown items or groups to search through.
   * @param {string} key The key of the item to find.
   * @returns {Nullable<RTEDropdownListItem>} The dropdown list item if found, otherwise null.
   */
  getListItemByKey(itemList, key) {
    for (const item of itemList) {
      if ('children' in item) {
        const foundItem = this.getListItemByKey(item.children, key);
        if (foundItem) {
          return foundItem;
        }
      }
      else if (item.key === key) {
        return item;
      }
    }
    return null;
  }
}
