/* eslint no-irregular-whitespace: 0 */
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
import keyboardJS from 'keyboardjs';
import classnames from 'classnames';
import copy from 'copy-to-clipboard';
import { JSONTree } from 'react-json-tree';
import { ExpireLocalStorage } from '@ablogcms/utils';
import { fetchClient } from '@ablogcms/fetch-client';
import useClipboard from '@ablogcms/react-hooks/use-clipboard';
import { Modal, ModalBody, ModalHeader, ModalFooter } from '@ablogcms/components/modal';
import { HStack } from '@ablogcms/components/stack';
import SyntaxHighlight from '@ablogcms/components/syntax-highlight';
import { Tooltip } from '@ablogcms/components/tooltip';
import VisuallyHidden from '@ablogcms/components/visually-hidden';
import IncrementalSearch from '../../lib/incremental-search';

// スニペット／V2モジュール変数モーダルのコピー結果をボタン自体のツールチップで示す
// （audit-log-detail-modal / media-modal と同じパターン）。モーダルは排他表示のため
// ツールチップ・id は 1 つを両ボタンで共有してよい
const COPY_TOOLTIP_ID = 'quick-search-copy-tooltip';
const COPY_TOOLTIP_RESET_MS = 2000;

// グローバル変数はリスト項目のクリック／Enter選択でコピーされ、専用の固定ボタンを持たない
// （モーダルも開かない）ため、クリック（Enter選択時はキーボード操作中の現在行）した項目自体に
// react-tooltip の命令的 API（anchorSelect）で動的にツールチップを出す
const GLOBAL_VARS_COPY_TOOLTIP_ID = 'quick-search-global-vars-copy-tooltip';
const GLOBAL_VARS_CURRENT_ITEM_ID = 'quick-search-global-vars-current-item';

const jsonTreeTheme = {
  scheme: 'OneDark',
  author: 'Lalit Magant (http://github.com/tilal6991)',
  base00: '#282c34',
  base01: '#353b45',
  base02: '#3e4451',
  base03: '#545862',
  base04: '#565c64',
  base05: '#abb2bf',
  base06: '#b6bdca',
  base07: '#c8ccd4',
  base08: '#e06c75',
  base09: '#d19a66',
  base0A: '#e5c07b',
  base0B: '#98c379',
  base0C: '#56b6c2',
  base0D: '#61afef',
  base0E: '#c678dd',
  base0F: '#be5046',
  tree: {
    margin: 0,
    padding: '10px', // ツリー全体のパディング
    borderRadius: '5px',
  },
};

function CustomJsonLabelRenderer({ keyPath }) {
  const handleClick = (e) => {
    e.preventDefault();
    if (keyPath) {
      const fixPath = keyPath
        .slice()
        .reverse()
        .map((key) => (typeof key === 'number' ? `[${key}]` : key))
        .join('.')
        .replace(/\.([[]\d+])/g, '$1'); // ドットの後にブラケットが続く場合のドットを削除
      copy(fixPath);
    }
  };
  return (
    <span
      onClick={handleClick}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          handleClick(e);
        }
      }}
      role="button"
      tabIndex={0}
      style={{ cursor: 'pointer' }}
    >
      {keyPath[0]}
    </span>
  );
}

function QuickSearch({ buttons }) {
  const [lists, setLists] = useState([]);
  const [init, setInit] = useState(false);
  const [menus, setMenus] = useState(null);
  const [snippets, setSnippets] = useState(null);
  const [vars, setVars] = useState(null);
  const [globalVars, setGlobalVars] = useState(null);
  const [number, setNumber] = useState(-1);
  const [isOpen, setIsOpen] = useState(false);
  const [keyword, setKeyword] = useState('');
  const [isSnippetsModalOpen, setIsSnippetsModalOpen] = useState(false);
  const [isVarsModalOpen, setIsVarsModalOpen] = useState(false);
  const [isV2ModuleVarsModalOpen, setIsV2ModuleVarsModalOpen] = useState(false);
  const [modalContent, setModalContent] = useState('');
  const [modalTitlte, setModalTitlte] = useState('');
  const [currentItem, setCurrentItem] = useState(null);
  const copyButtonRef = useRef(null);
  const searchRef = useRef(null);
  const boxRef = useRef(null);
  const globalVarsTooltipRef = useRef(null);
  const globalVarsTooltipTimerRef = useRef(null);
  const { copy: copyWithFeedback, copied: copiedForTooltip, reset: resetCopiedTooltip } = useClipboard();
  const { copy: copyGlobalVarWithFeedback } = useClipboard();
  const isMacOs = navigator.userAgent.match(/Mac|PPC/);
  let v2ModuleVars = null;
  let is = null;

  const moduleEmbeddedJsonDom = document.getElementById('acms-module-data');
  if (moduleEmbeddedJsonDom && moduleEmbeddedJsonDom.dataset.json) {
    v2ModuleVars = JSON.parse(moduleEmbeddedJsonDom.dataset.json);
  }

  const handleAfterOpen = () => {
    is = new IncrementalSearch();
    is.addRequest(
      searchRef.current,
      ACMS.Library.acmsLink({
        bid: ACMS.Config.bid,
      }),
      (data) => {
        setLists(data);
      }
    );
  };

  const handleAfterClose = () => {
    if (is) {
      is.destroy();
    }
    is = null;
  };

  const loadGlobalVars = () => {
    const params = new URLSearchParams();
    params.append('ACMS_POST_Search_GlobalVars', true);
    params.append('formToken', window.csrfToken);
    fetchClient.post(window.location.href, params).then((res) => {
      res.data.items.push({
        bid: ACMS.Config.bid,
        title: '\u0025{ROOT_TPL}',
        subtitle: ACMS.i18n('quick_search.root_tpl'),
        url: ACMS.Config.rootTpl,
      });
      setGlobalVars(res.data);
    });
  };

  const setDefinedLists = useCallback(() => {
    const endpoint = `${ACMS.Library.acmsLink({
      tpl: 'acms-code/all.json',
    })}?cache=${new Date().getTime()}`;

    const key = `acms_big_${ACMS.Config.bid}_quick_search_data`;
    const storage = new ExpireLocalStorage();
    const data = storage.load(key);
    if (data) {
      setMenus(data.menus);
      setSnippets(data.snippets);
      setVars(data.vars);
      loadGlobalVars();
    } else {
      fetchClient
        .get(endpoint)
        .then((res) => {
          setMenus(res.data.menus);
          setSnippets(res.data.snippets);
          setVars(res.data.vars);
          storage.save(key, res.data, 1800);
        })
        .then(() => {
          loadGlobalVars();
        });
    }
  }, []);

  const getMode = useMemo(() => {
    if (ACMS.Config.auth !== 'administrator') {
      return 'normal';
    }
    if (keyword.slice(0, 1) === ';') {
      return 'snippets';
    }
    if (keyword.slice(0, 1) === ':') {
      return 'vars';
    }
    if (keyword.slice(0, 1) === '%') {
      return 'global-vars';
    }
    if (keyword.slice(0, 1) === '#') {
      return 'v2module-vars';
    }
    return 'normal';
  }, [keyword]);

  const handleClickEvent = useCallback(
    (item, event) => {
      if (getMode === 'snippets') {
        showSnippets(item);
      } else if (getMode === 'vars') {
        showVars(item);
      } else if (getMode === 'global-vars') {
        showGlobalVars(item, event);
      } else if (getMode === 'v2module-vars') {
        showV2ModuleVars(item);
      } else {
        gotoLink(item);
      }
    },
    // showSnippets/showVars/showGlobalVars/showV2ModuleVars/gotoLink はいずれもレンダーごとに
    // 再生成される非メモ化関数で、意図的に依存配列へ含めていない（同ファイル内の既存踏襲）。
    // showGlobalVars は宣言順の関係で依存配列に含めると参照時に TDZ エラーになるため、
    // コード全体の再構成をしない限り追加できない
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [getMode]
  );

  const gotoLink = (item) => {
    if (item) {
      location.href = item.url;
    }
  };

  const showSnippets = (item) => {
    if (item) {
      fetchClient.get(item.url).then((res) => {
        const parser = new DOMParser();
        let html = parser.parseFromString(res.data, 'text/html');
        html = (html.querySelector('pre>code') || html.querySelector('textarea')).textContent;
        setModalContent(html);
        setIsSnippetsModalOpen(true);
      });
    }
  };

  const showVars = (item) => {
    if (item) {
      fetchClient.get(item.url).then((res) => {
        const parser = new DOMParser();
        const html = parser.parseFromString(res.data, 'text/html');
        setModalContent(html.body.innerHTML);
        setIsVarsModalOpen(true);
      });
    }
  };

  const showV2ModuleVars = (item) => {
    if (item) {
      setModalTitlte(item.title + (item.subtitle ? `（${item.subtitle}）` : ''));
      setModalContent(item.data);
      setIsV2ModuleVarsModalOpen(true);
    }
  };

  const showGlobalVars = (item, event) => {
    if (item) {
      copyGlobalVarWithFeedback(item.title);
      // 実際にクリックされたポインター位置から出す。マウスクリックなら event.clientX/Y を使い、
      // キーボード操作（Enter選択）で event が無い場合のみ、現在行の中心位置にフォールバックする
      clearTimeout(globalVarsTooltipTimerRef.current);
      let position;
      if (event && typeof event.clientX === 'number') {
        position = { x: event.clientX, y: event.clientY };
      } else if (currentItem) {
        const rect = currentItem.getBoundingClientRect();
        position = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
      }
      globalVarsTooltipRef.current?.open({
        content: ACMS.i18n('quick_search.copy_message'),
        ...(position ? { position } : { anchorSelect: `#${GLOBAL_VARS_CURRENT_ITEM_ID}` }),
      });
      globalVarsTooltipTimerRef.current = setTimeout(() => {
        globalVarsTooltipRef.current?.close();
      }, COPY_TOOLTIP_RESET_MS);
    }
  };

  const getFilteredSnippets = useMemo(() => {
    const searchWord = keyword ? keyword.toLowerCase().slice(1) : '';

    if (snippets && snippets.items && searchWord) {
      const items = snippets.items.filter((item) => {
        if (
          item.title.toLowerCase().indexOf(searchWord) !== -1 ||
          item.subtitle.toLowerCase().indexOf(searchWord) !== -1
        ) {
          return true;
        }
        return false;
      });
      return {
        title: snippets.title,
        enTitle: snippets.enTitle,
        items,
      };
    }
    return snippets;
  }, [keyword, snippets]);

  const getFilteredVars = useMemo(() => {
    const searchWord = keyword ? keyword.toLowerCase().slice(1) : '';

    if (vars && vars.items && searchWord) {
      const items = vars.items.filter((item) => {
        if (
          item.title.toLowerCase().indexOf(searchWord) !== -1 ||
          item.subtitle.toLowerCase().indexOf(searchWord) !== -1
        ) {
          return true;
        }
        return false;
      });
      return {
        title: vars.title,
        enTitle: vars.enTitle,
        items,
      };
    }
    return vars;
  }, [keyword, vars]);

  const getFilteredGlobalVars = useMemo(() => {
    const searchWord = keyword ? keyword.toLowerCase().slice(1) : '';

    if (globalVars && globalVars.items && searchWord) {
      const items = globalVars.items.filter((item) => {
        if (
          item.title.toLowerCase().indexOf(searchWord) !== -1 ||
          item.subtitle.toLowerCase().indexOf(searchWord) !== -1
        ) {
          return true;
        }
        return false;
      });
      return {
        title: globalVars.title,
        enTitle: globalVars.enTitle,
        items,
      };
    }
    return globalVars;
  }, [keyword, globalVars]);

  const getFilteredV2ModuleVars = useMemo(() => {
    const searchWord = keyword ? keyword.toLowerCase().slice(1) : '';

    if (v2ModuleVars && v2ModuleVars.items && searchWord) {
      const items = v2ModuleVars.items.filter((item) => {
        if (
          item.title.toLowerCase().indexOf(searchWord) !== -1 ||
          item.subtitle.toLowerCase().indexOf(searchWord) !== -1
        ) {
          return true;
        }
        return false;
      });
      return {
        title: v2ModuleVars.title,
        enTitle: v2ModuleVars.enTitle,
        items,
      };
    }
    return v2ModuleVars;
  }, [keyword, v2ModuleVars]);

  const getFilteredMenus = useMemo(() => {
    if (menus && menus.items) {
      const items = menus.items.filter((item) => {
        if (!keyword) {
          return false;
        }
        if (item.title.indexOf(keyword) !== -1 || item.subtitle.indexOf(keyword) !== -1) {
          return true;
        }
        return false;
      });
      return {
        title: menus.title,
        enTitle: menus.enTitle,
        items,
      };
    }
    return menus;
  }, [keyword, menus]);

  const getCombindLists = useCallback(() => {
    if (getMode === 'snippets') {
      return [getFilteredSnippets];
    }
    if (getMode === 'vars') {
      return [getFilteredVars];
    }
    if (getMode === 'global-vars') {
      return [getFilteredGlobalVars];
    }
    if (getMode === 'v2module-vars') {
      return getFilteredV2ModuleVars ? [getFilteredV2ModuleVars] : [];
    }
    if (getFilteredMenus) {
      return [getFilteredMenus, ...lists];
    }
    return lists;
  }, [
    getFilteredSnippets,
    getFilteredVars,
    getFilteredGlobalVars,
    getFilteredV2ModuleVars,
    getFilteredMenus,
    lists,
    getMode,
  ]);

  const getNumber = useCallback(
    (listIndex, index) => {
      const combindLists = getCombindLists();
      let num = 0;
      while (listIndex > 0) {
        listIndex--;
        if (combindLists[listIndex]) {
          if (combindLists[listIndex].items) {
            num += combindLists[listIndex].items.length;
          }
        }
      }
      return num + index;
    },
    [getCombindLists]
  );

  const handleSetKeyword = (keyword) => {
    setKeyword(keyword);
    setNumber(0);
  };

  const closeDialog = () => {
    setIsOpen(false);
    setKeyword('');
    setLists([]);
  };

  const openDialog = () => {
    setIsOpen(true);
    setKeyword('');
    setLists([]);
  };

  const toggleDialog = useCallback(() => {
    if (init === false) {
      setInit(true);
      setDefinedLists();
    }
    if (isOpen) {
      closeDialog();
    } else {
      openDialog();
    }
  }, [init, isOpen, setDefinedLists]);

  const handleClose = () => {
    closeDialog();
  };

  const gotoNextItem = useCallback(() => {
    const combindLists = getCombindLists();
    const maxNumber = getNumber(combindLists.length, 0) - 1;
    const nextNumber = number + 1 > maxNumber ? 0 : number + 1;
    setNumber(nextNumber);
  }, [number, getCombindLists, getNumber]);

  const gotoPrevItem = useCallback(() => {
    const combindLists = getCombindLists();
    const maxNumber = getNumber(combindLists.length, 0) - 1;
    if (!isOpen) {
      return;
    }
    const nextNumber = number - 1 < 0 ? maxNumber : number - 1;
    setNumber(nextNumber);
  }, [number, getCombindLists, getNumber, isOpen]);

  const getCurrentItem = useCallback(() => {
    const combindLists = getCombindLists();
    let itemNum = 0;
    let res = false;
    combindLists.forEach((list) => {
      list.items.forEach((item) => {
        if (number === itemNum) {
          res = item;
        }
        itemNum++;
      });
    });
    return res;
  }, [number, getCombindLists]);

  const handleSetNumber = (listIndex, index) => {
    const number = getNumber(listIndex, index);
    setNumber(number);
  };

  const handleCloseSnippetsModal = () => {
    setIsSnippetsModalOpen(false);
    // setKeyword(keyword.replace(/^(:|;|#)(.*)/g, '$1'));
    requestAnimationFrame(() => {
      if (searchRef.current) {
        searchRef.current.focus();
      }
    });
  };

  const handleCloseVarsModal = () => {
    setIsVarsModalOpen(false);
    // setKeyword(keyword.replace(/^(:|;|#)(.*)/g, '$1'));
    requestAnimationFrame(() => {
      if (searchRef.current) {
        searchRef.current.focus();
      }
    });
  };

  const handleCloseV2ModuleVarsModal = () => {
    setIsV2ModuleVarsModalOpen(false);
    // setKeyword(keyword.replace(/^(:|;|#)(.*)/g, '$1'));
    requestAnimationFrame(() => {
      if (searchRef.current) {
        searchRef.current.focus();
      }
    });
  };

  const handleCopySnippet = () => {
    copyWithFeedback(modalContent);
    setTimeout(resetCopiedTooltip, COPY_TOOLTIP_RESET_MS);
  };

  const handleCopyV2ModuleVars = () => {
    copyWithFeedback(JSON.stringify(modalContent, null, 2));
    setTimeout(resetCopiedTooltip, COPY_TOOLTIP_RESET_MS);
  };

  const getInitialClassByName = (name) => {
    switch (name) {
      case 'Blogs':
        return 'acms-admin-quick-search-initial-b';
      case 'Categories':
        return 'acms-admin-quick-search-initial-c';
      case 'Entries':
        return 'acms-admin-quick-search-initial-e';
      case 'Modules':
        return 'acms-admin-quick-search-initial-m';
      case 'Menu':
        return 'acms-admin-quick-search-initial-m2';
      case 'Vars':
        return 'acms-admin-quick-search-initial-v';
      case 'Snippets':
        return 'acms-admin-quick-search-initial-s';
      case 'Global vars':
        return 'acms-admin-quick-search-initial-g';
      case 'V2 Module vars':
        return 'acms-admin-quick-search-initial-v2';
      default:
        return '';
    }
  };

  useEffect(() => {
    if (buttons && buttons.length > 0) {
      buttons.forEach((button) => {
        button.addEventListener('click', toggleDialog);
      });
    }
    keyboardJS.bind(ACMS.Config.quickSearchCommand, (e) => {
      e.preventDefault();
      toggleDialog();
    });
    keyboardJS.bind(['tab', 'down'], (e) => {
      if (isOpen && !isSnippetsModalOpen && !isVarsModalOpen && !isV2ModuleVarsModalOpen) {
        e.preventDefault();
        gotoNextItem();
      }
    });
    keyboardJS.bind(['shift + tab', 'up'], (e) => {
      if (isOpen && !isSnippetsModalOpen && !isVarsModalOpen && !isV2ModuleVarsModalOpen) {
        e.preventDefault();
        gotoPrevItem();
      }
    });
    keyboardJS.bind(['enter'], (e) => {
      if (isOpen && !isSnippetsModalOpen && !isVarsModalOpen && !isV2ModuleVarsModalOpen) {
        e.preventDefault();
        const item = getCurrentItem();
        handleClickEvent(item);
      }
    });
    return () => {
      if (buttons && buttons.length > 0) {
        buttons.forEach((button) => {
          button.removeEventListener('click', toggleDialog);
        });
      }
      keyboardJS.reset();
    };
  }, [
    isOpen,
    isSnippetsModalOpen,
    isVarsModalOpen,
    isV2ModuleVarsModalOpen,
    buttons,
    getCurrentItem,
    toggleDialog,
    handleClickEvent,
    gotoNextItem,
    gotoPrevItem,
  ]);

  useEffect(() => {
    if (currentItem && boxRef.current) {
      const boxTop = boxRef.current.getBoundingClientRect().top;
      const boxBottom = boxTop + boxRef.current.offsetHeight;
      const itemTop = currentItem.getBoundingClientRect().top;
      const itemBottom = itemTop + currentItem.offsetHeight;
      const positionTop = itemTop - boxTop;
      const positionBottom = boxBottom - itemBottom;
      if (positionTop < 0 || positionBottom < 0) {
        boxRef.current.scrollTop += positionTop;
      }
    }
  }, [currentItem]);

  const renderJsonLabel = (keyPath, nodeType, expanded, expandable) => {
    return <CustomJsonLabelRenderer keyPath={keyPath} />;
  };

  return (
    <>
      <Modal
        isOpen={isOpen}
        onClose={handleClose}
        id="quick-search-dialog"
        className="acms-admin-modal-middle"
        dialogClassName="acms-admin-modal-quick-search"
        onAfterOpen={handleAfterOpen}
        onAfterClose={handleAfterClose}
        aria-labelledby="acms-qucik-search-dialog-title"
      >
        <ModalBody>
          <div className="acms-admin-form" style={{ paddingTop: '15px', paddingBottom: '15px' }}>
            <label className="acms-admin-width-max">
              <VisuallyHidden id="acms-qucik-search-dialog-title">{ACMS.i18n('quick_search.title')}</VisuallyHidden>
              <input
                type="text"
                ref={searchRef}
                style={{ fontSize: '24px', fontWeight: 'bold' }}
                placeholder={ACMS.i18n('quick_search.input_placeholder')}
                className="acms-admin-form-width-full acms-admin-form-large acms-admin-margin-bottom-small"
                onInput={(e) => {
                  handleSetKeyword(e.target.value);
                }}
              />
              <Tooltip ref={globalVarsTooltipRef} id={GLOBAL_VARS_COPY_TOOLTIP_ID} variant="dark" />
            </label>
          </div>
          <div ref={boxRef} className="acms-admin-modal-middle-scroll">
            {getCombindLists().map((list, listIndex) => (
              <div key={`label-${list.enTitle}`}>
                {list && list.items.length > 0 && (
                  <div>
                    <h2 className="acms-admin-admin-title3" style={{ background: '#FFF' }}>
                      {list.title}
                    </h2>
                    <table className="acms-admin-table-admin acms-admin-form acms-admin-table-hover">
                      <tbody key={`body-${list.enTitle}`}>
                        {list.items.map((item, index) => (
                          <tr
                            key={getNumber(listIndex, index)}
                            id={getNumber(listIndex, index) === number ? GLOBAL_VARS_CURRENT_ITEM_ID : undefined}
                            ref={(element) => {
                              if (getNumber(listIndex, index) === number) setCurrentItem(element);
                            }}
                            onMouseMove={() => handleSetNumber(listIndex, index)}
                            className={classnames({ hover: getNumber(listIndex, index) === number })}
                          >
                            <td style={{ width: '1px', wordBreak: 'break-all' }}>
                              {}
                              <div
                                className={classnames(
                                  'acms-admin-quick-search-initial-mark',
                                  getInitialClassByName(list.enTitle)
                                )}
                              />
                            </td>
                            <td>
                              {getMode === 'normal' ? (
                                <a href={item.url}>
                                  <span className="acms-admin-quick-search-main-title">{item.title}</span>
                                  <span className="acms-admin-quick-search-sub-title">
                                    {item.subtitle} <span>{item.blogName}</span>
                                  </span>
                                </a>
                              ) : (
                                <button
                                  className="acms-admin-quick-search-title-btn"
                                  type="button"
                                  onClick={(event) => handleClickEvent(item, event)}
                                >
                                  <span className="acms-admin-quick-search-main-title">{item.title}</span>
                                  <span className="acms-admin-quick-search-sub-title">
                                    {item.subtitle} <span>{item.blogName}</span>
                                  </span>
                                </button>
                              )}
                            </td>
                            {getMode !== 'normal' ? (
                              <td style={{ textAlign: 'right', wordBreak: 'break-all' }}>
                                {getMode === 'global-vars' && <span style={{ paddingRight: '10px' }}>{item.url}</span>}
                              </td>
                            ) : (
                              <td style={{ width: '1px', textAlign: 'right', whiteSpace: 'nowrap' }}>
                                {item.bid && (
                                  <span className="acms-admin-label">
                                    bid:
                                    {item.bid}
                                  </span>
                                )}
                                {item.cid && (
                                  <span className="acms-admin-label">
                                    cid:
                                    {item.cid}
                                  </span>
                                )}
                                {item.eid && (
                                  <span className="acms-admin-label">
                                    eid:
                                    {item.eid}
                                  </span>
                                )}
                                {item.mid && (
                                  <span className="acms-admin-label">
                                    mid:
                                    {item.mid}
                                  </span>
                                )}
                              </td>
                            )}
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </div>
                )}
              </div>
            ))}
          </div>
        </ModalBody>
        <ModalFooter>
          <ul className="acms-admin-list-inline">
            <li>
              <kbd>tab</kbd> or
              <kbd>⇅</kbd> {ACMS.i18n('quick_search.choice')}
            </li>
            <li>
              <kbd>↵</kbd> {ACMS.i18n('quick_search.move')}
            </li>
            <li>
              <kbd>esc</kbd> {ACMS.i18n('quick_search.close')}
            </li>
            {ACMS.Config.auth === 'administrator' && (
              <>
                <li>
                  <kbd>{isMacOs ? <span>⌘K</span> : <span>ctl+k</span>}</kbd> {ACMS.i18n('quick_search.open')}
                </li>
                <li>
                  <kbd>;</kbd> {ACMS.i18n('quick_search.snippets')}
                </li>
                <li>
                  <kbd>#</kbd> {ACMS.i18n('quick_search.v2module_vars')}
                </li>
                <li>
                  <kbd>:</kbd> {ACMS.i18n('quick_search.vars')}
                </li>
                <li>
                  <kbd>%</kbd> {ACMS.i18n('quick_search.g_vars')}
                </li>
              </>
            )}
          </ul>
        </ModalFooter>
      </Modal>
      <Modal
        isOpen={isSnippetsModalOpen}
        onClose={handleCloseSnippetsModal}
        focusTrapOptions={{ initialFocus: () => copyButtonRef.current }}
      >
        <ModalHeader>{ACMS.i18n('quick_search.snippets')}</ModalHeader>
        <ModalBody>
          <div className="acms-admin-form">
            <SyntaxHighlight language="twig" style={{ height: '400px', overflow: 'scroll', borderRadius: '5px' }}>
              {modalContent}
            </SyntaxHighlight>
          </div>
        </ModalBody>
        <ModalFooter>
          <HStack display="inline-flex">
            <button type="button" onClick={handleCloseSnippetsModal} className="acms-admin-btn">
              {ACMS.i18n('quick_search.close')}
            </button>
            <button
              ref={copyButtonRef}
              type="button"
              onClick={handleCopySnippet}
              className="acms-admin-btn"
              data-tooltip-id={COPY_TOOLTIP_ID}
              data-tooltip-content={ACMS.i18n('quick_search.copy_message')}
            >
              {ACMS.i18n('quick_search.copy')}
            </button>
          </HStack>
        </ModalFooter>
      </Modal>
      <Modal isOpen={isVarsModalOpen} onClose={handleCloseVarsModal}>
        <ModalHeader>{ACMS.i18n('quick_search.vars')}</ModalHeader>
        <ModalBody>
          <div className="acms-admin-form">
            <div style={{ paddingTop: '10px', paddingBottom: '10px' }}>
              <div
                className="acms-admin-quick-search-variable-table"
                dangerouslySetInnerHTML={{ __html: modalContent }}
              />
            </div>
          </div>
        </ModalBody>
        <ModalFooter>
          <div>
            <button type="button" onClick={handleCloseVarsModal} className="acms-admin-btn">
              {ACMS.i18n('quick_search.close')}
            </button>
          </div>
        </ModalFooter>
      </Modal>
      <Modal isOpen={isV2ModuleVarsModalOpen} onClose={handleCloseV2ModuleVarsModal}>
        <ModalHeader>{modalTitlte}</ModalHeader>
        <ModalBody>
          <div className="acms-admin-form">
            <div style={{ paddingTop: '10px', paddingBottom: '10px' }}>
              <JSONTree
                data={modalContent}
                theme={jsonTreeTheme}
                hideRoot
                invertTheme={false}
                shouldExpandNodeInitially={(keyPath, data, level) => {
                  return level < 1;
                }}
                labelRenderer={renderJsonLabel}
                style={{ padding: '10px' }}
              />
            </div>
          </div>
        </ModalBody>
        <ModalFooter>
          <HStack display="inline-flex">
            <button type="button" onClick={handleCloseV2ModuleVarsModal} className="acms-admin-btn">
              {ACMS.i18n('quick_search.close')}
            </button>
            <button
              ref={copyButtonRef}
              type="button"
              onClick={handleCopyV2ModuleVars}
              className="acms-admin-btn"
              data-tooltip-id={COPY_TOOLTIP_ID}
              data-tooltip-content={ACMS.i18n('quick_search.copy_message')}
            >
              {ACMS.i18n('quick_search.copy')}
            </button>
          </HStack>
        </ModalFooter>
      </Modal>
      <Tooltip
        id={COPY_TOOLTIP_ID}
        isOpen={copiedForTooltip}
        openEvents={{ mouseover: false, focus: false }}
        variant="dark"
      />
    </>
  );
}

export default QuickSearch;
