// Nova toolbar interactions
// - dropdown menus via [data-dropdown]
// - mobile menu toggle via [data-mobile-toggle] + #mobileMenu

// Alpine.js — powers x-data/x-show/@click in Blade layouts (e.g. cookie banner, toasts).
// Guard: don't start a second instance if app.js already loaded Alpine on this page.
import Alpine from 'alpinejs';
if (!window.Alpine) {
  window.Alpine = Alpine;
  Alpine.start();
}

// Gallery navigation context: stores artwork list for prev/next on artwork page
import './lib/nav-context.js';
import { sendTagInteractionEvent } from './lib/tagAnalytics';

function safeParseJson(value, fallback) {
  try {
    return JSON.parse(value || 'null') ?? fallback;
  } catch (_error) {
    return fallback;
  }
}

var reactRuntimePromise = null;

function getReactRuntime() {
  if (!reactRuntimePromise) {
    reactRuntimePromise = Promise.all([
      import('react'),
      import('react-dom/client'),
    ]).then(function (modules) {
      return {
        React: modules[0].default,
        createRoot: modules[1].createRoot,
      };
    });
  }

  return reactRuntimePromise;
}

function mountStoryEditor() {
  var storyEditorRoot = document.getElementById('story-editor-react-root');
  if (!storyEditorRoot) return;
  if (storyEditorRoot.dataset.reactMounted === 'true') return;

  var mode = storyEditorRoot.getAttribute('data-mode') || 'create';
  var storyRaw = storyEditorRoot.getAttribute('data-story') || '{}';
  var storyTypesRaw = storyEditorRoot.getAttribute('data-story-types') || '[]';
  var endpointsRaw = storyEditorRoot.getAttribute('data-endpoints') || '{}';
  var csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';

  var initialStory = {};
  var storyTypes = [];
  var endpoints = {};

  try {
    initialStory = JSON.parse(storyRaw);
    storyTypes = JSON.parse(storyTypesRaw);
    endpoints = JSON.parse(endpointsRaw);
  } catch (_error) {
    // If parsing fails, the editor falls back to component defaults.
  }

  storyEditorRoot.dataset.reactMounted = 'true';

  void Promise.all([
    import('./components/editor/StoryEditor'),
    getReactRuntime(),
  ])
    .then(function (resolved) {
      var module = resolved[0];
      var reactRuntime = resolved[1];
      var StoryEditor = module.default;
      reactRuntime.createRoot(storyEditorRoot).render(
        reactRuntime.React.createElement(StoryEditor, {
          mode: mode,
          initialStory: initialStory,
          storyTypes: storyTypes,
          endpoints: endpoints,
          csrfToken: csrfToken,
        })
      );
    })
    .catch(function () {
      storyEditorRoot.dataset.reactMounted = 'false';
      storyEditorRoot.innerHTML = '<div class="rounded-xl border border-rose-700 bg-rose-900/20 p-4 text-rose-200">Failed to load editor. Please refresh the page.</div>';
    });
}

mountStoryEditor();

function mountToolbarNotifications() {
  var rootEl = document.getElementById('toolbar-notification-root');
  if (!rootEl || rootEl.dataset.reactMounted === 'true') return;

  var props = safeParseJson(rootEl.getAttribute('data-props'), {});
  rootEl.dataset.reactMounted = 'true';

  void Promise.all([
    import('./components/social/NotificationDropdown.jsx'),
    getReactRuntime(),
  ])
    .then(function (resolved) {
      var module = resolved[0];
      var reactRuntime = resolved[1];
      var Component = module.default;
      reactRuntime.createRoot(rootEl).render(reactRuntime.React.createElement(Component, props));
    })
    .catch(function () {
      rootEl.dataset.reactMounted = 'false';
    });
}

function mountToolbarMessages() {
  var rootEl = document.getElementById('toolbar-messages-root');
  if (!rootEl || rootEl.dataset.reactMounted === 'true') return;

  var props = safeParseJson(rootEl.getAttribute('data-props'), {});
  rootEl.dataset.reactMounted = 'true';

  void Promise.all([
    import('./components/social/MessageInboxBadge.jsx'),
    getReactRuntime(),
  ])
    .then(function (resolved) {
      var module = resolved[0];
      var reactRuntime = resolved[1];
      var Component = module.default;
      reactRuntime.createRoot(rootEl).render(reactRuntime.React.createElement(Component, props));
    })
    .catch(function () {
      rootEl.dataset.reactMounted = 'false';
    });
}

function mountStorySocial() {
  var socialRoot = document.getElementById('story-social-root');
  if (socialRoot && socialRoot.dataset.reactMounted !== 'true') {
    var props = safeParseJson(socialRoot.getAttribute('data-props'), {});
    socialRoot.dataset.reactMounted = 'true';

    void Promise.all([
      import('./components/social/StorySocialPanel.jsx'),
      getReactRuntime(),
    ])
      .then(function (resolved) {
        var module = resolved[0];
        var reactRuntime = resolved[1];
        var Component = module.default;
        reactRuntime.createRoot(socialRoot).render(reactRuntime.React.createElement(Component, {
          story: props.story,
          creator: props.creator,
          initialState: props.state,
          initialComments: props.comments,
          isAuthenticated: Boolean(props.is_authenticated),
        }));
      })
      .catch(function () {
        socialRoot.dataset.reactMounted = 'false';
      });
  }

  var followRoot = document.getElementById('story-creator-follow-root');
  if (!followRoot || followRoot.dataset.reactMounted === 'true') return;

  var followProps = safeParseJson(followRoot.getAttribute('data-props'), {});
  followRoot.dataset.reactMounted = 'true';

  void Promise.all([
    import('./components/social/FollowButton.jsx'),
    getReactRuntime(),
  ])
    .then(function (resolved) {
      var module = resolved[0];
      var reactRuntime = resolved[1];
      var Component = module.default;
      reactRuntime.createRoot(followRoot).render(reactRuntime.React.createElement(Component, {
        username: followProps.username,
        initialFollowing: Boolean(followProps.following),
        initialCount: Number(followProps.followers_count || 0),
        className: 'w-full justify-center',
      }));
    })
    .catch(function () {
      followRoot.dataset.reactMounted = 'false';
    });
}

mountToolbarMessages();
mountToolbarNotifications();
mountStorySocial();

function initStorySyntaxHighlighting() {
  var codeBlocks = Array.prototype.slice.call(document.querySelectorAll('.story-prose pre code'));
  if (!codeBlocks.length) return;

  function fallbackCopyText(text) {
    var textarea = document.createElement('textarea');
    textarea.value = text;
    textarea.setAttribute('readonly', 'true');
    textarea.style.position = 'fixed';
    textarea.style.top = '-1000px';
    textarea.style.left = '-1000px';
    document.body.appendChild(textarea);
    textarea.select();

    try {
      return document.execCommand('copy');
    } catch (_error) {
      return false;
    } finally {
      document.body.removeChild(textarea);
    }
  }

  function copyText(text) {
    if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
      return navigator.clipboard.writeText(text);
    }

    return fallbackCopyText(text)
      ? Promise.resolve()
      : Promise.reject(new Error('Clipboard unavailable'));
  }

  function attachCopyButton(block) {
    var pre = block.parentElement;
    if (!pre || pre.dataset.copyButtonMounted === 'true') return;

    var button = document.createElement('button');
    var icon = document.createElement('span');
    var label = document.createElement('span');

    button.type = 'button';
    button.className = 'story-code-copy-button';
    icon.className = 'story-code-copy-icon';
    icon.setAttribute('aria-hidden', 'true');
    icon.textContent = '⧉';
    label.className = 'story-code-copy-label';
    label.textContent = 'Copy';
    button.appendChild(icon);
    button.appendChild(label);
    button.dataset.copied = 'idle';
    button.setAttribute('aria-label', 'Copy code block');

    var resetTimer = 0;
    button.addEventListener('click', function () {
      var source = block.innerText || block.textContent || '';

      copyText(source)
        .then(function () {
          icon.textContent = '✓';
          label.textContent = 'Copied';
          button.dataset.copied = 'true';
        })
        .catch(function () {
          icon.textContent = '!';
          label.textContent = 'Failed';
          button.dataset.copied = 'false';
        })
        .finally(function () {
          window.clearTimeout(resetTimer);
          resetTimer = window.setTimeout(function () {
            icon.textContent = '⧉';
            label.textContent = 'Copy';
            button.dataset.copied = 'idle';
          }, 1800);
        });
    });

    pre.appendChild(button);
    pre.dataset.copyButtonMounted = 'true';
  }

  void import('highlight.js/lib/common')
    .then(function (module) {
      var hljs = module.default;

      codeBlocks.forEach(function (block) {
        attachCopyButton(block);

        if (block.dataset.syntaxHighlighted === 'true') return;

        var language = (block.getAttribute('data-language') || '').trim();
        if (language && !block.className.includes('language-')) {
          block.classList.add('language-' + language);
        }

        hljs.highlightElement(block);
        block.dataset.syntaxHighlighted = 'true';
      });
    })
    .catch(function () {
      // Leave code blocks readable even if highlighting fails to load.
    });
}

initStorySyntaxHighlighting();

function initTagsSearchAssist() {
  var roots = document.querySelectorAll('[data-tags-search-root]');
  if (!roots.length) return;

  var recentSearchesKey = 'skinbase.tags.recent-searches';

  function findClosest(el, selector) {
    while (el && el.nodeType === 1) {
      if (el.matches(selector)) return el;
      el = el.parentElement;
    }
    return null;
  }

  function escapeHtml(value) {
    return String(value || '')
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }

  roots.forEach(function (root, rootIndex) {
    var form = findClosest(root, '[data-tags-search-form]');
    var input = root.querySelector('[data-tags-search-input]');
    var panel = root.querySelector('[data-tags-search-panel]');
    var title = root.querySelector('[data-tags-search-title]');
    var results = root.querySelector('[data-tags-search-results]');
    var endpoint = root.getAttribute('data-search-endpoint') || '/api/tags/search';
    var popularEndpoint = root.getAttribute('data-popular-endpoint') || '/api/tags/popular';
    var optionIdPrefix = 'tags-search-option-' + rootIndex + '-';
    var debounceTimer = null;
    var abortController = null;
    var latestQuery = '';
    var activeIndex = -1;

    if (!input || !panel || !results) return;

    function readRecentSearches() {
      try {
        var parsed = JSON.parse(window.localStorage.getItem(recentSearchesKey) || '[]');
        return Array.isArray(parsed) ? parsed.filter(Boolean).slice(0, 5) : [];
      } catch (_error) {
        return [];
      }
    }

    function writeRecentSearch(query) {
      var normalized = (query || '').trim();
      if (!normalized) return;

      var next = readRecentSearches().filter(function (item) {
        return item.toLowerCase() !== normalized.toLowerCase();
      });

      next.unshift(normalized);

      try {
        window.localStorage.setItem(recentSearchesKey, JSON.stringify(next.slice(0, 5)));
      } catch (_error) {
        // Ignore storage failures silently.
      }
    }

    function clearRecentSearches() {
      try {
        window.localStorage.removeItem(recentSearchesKey);
      } catch (_error) {
        // Ignore storage failures silently.
      }
    }

    function removeRecentSearch(query) {
      var normalized = (query || '').trim().toLowerCase();
      if (!normalized) return;

      var next = readRecentSearches().filter(function (item) {
        return item.toLowerCase() !== normalized;
      });

      try {
        window.localStorage.setItem(recentSearchesKey, JSON.stringify(next));
      } catch (_error) {
        // Ignore storage failures silently.
      }
    }

    function getItems() {
      return Array.prototype.slice.call(results.querySelectorAll('[data-tags-search-item]'));
    }

    function setActiveItem(nextIndex) {
      var items = getItems();
      activeIndex = nextIndex;

      items.forEach(function (item, index) {
        var active = index === nextIndex;
        item.classList.toggle('bg-white/[0.06]', active);
        item.classList.toggle('text-white', active);
        item.setAttribute('aria-selected', active ? 'true' : 'false');
        if (!item.id) {
          item.id = optionIdPrefix + index;
        }
        if (active) {
          item.scrollIntoView({ block: 'nearest' });
        }
      });

      if (nextIndex >= 0 && items[nextIndex]) {
        input.setAttribute('aria-activedescendant', items[nextIndex].id);
      } else {
        input.removeAttribute('aria-activedescendant');
      }
    }

    function focusItem(nextIndex) {
      var items = getItems();
      if (!items.length) return;

      var boundedIndex = Math.max(0, Math.min(nextIndex, items.length - 1));
      setActiveItem(boundedIndex);
      items[boundedIndex].focus();
    }

    function setExpanded(expanded) {
      input.setAttribute('aria-expanded', expanded ? 'true' : 'false');
      panel.classList.toggle('hidden', !expanded);
      if (!expanded) {
        activeIndex = -1;
        setActiveItem(-1);
      }
    }

    function clearResults() {
      results.innerHTML = '';
      activeIndex = -1;
    }

    function hidePanel() {
      setExpanded(false);
    }

    function renderLoadingState(query) {
      title.textContent = query ? 'Searching tags' : 'Loading suggestions';
      results.setAttribute('aria-busy', 'true');
      results.innerHTML = '<div class="space-y-2 px-2 py-2">'
        + '<div class="flex items-center justify-between gap-3 rounded-xl px-3 py-3">'
        + '<div class="flex items-center gap-3"><span class="h-8 w-8 animate-pulse rounded-full bg-white/[0.08]"></span><span class="space-y-1"><span class="block h-3 w-24 animate-pulse rounded bg-white/[0.08]"></span><span class="block h-2.5 w-16 animate-pulse rounded bg-white/[0.05]"></span></span></div>'
        + '<span class="h-3 w-10 animate-pulse rounded bg-white/[0.06]"></span>'
        + '</div>'
        + '<div class="flex items-center justify-between gap-3 rounded-xl px-3 py-3">'
        + '<div class="flex items-center gap-3"><span class="h-8 w-8 animate-pulse rounded-full bg-white/[0.08]"></span><span class="space-y-1"><span class="block h-3 w-28 animate-pulse rounded bg-white/[0.08]"></span><span class="block h-2.5 w-20 animate-pulse rounded bg-white/[0.05]"></span></span></div>'
        + '<span class="h-3 w-12 animate-pulse rounded bg-white/[0.06]"></span>'
        + '</div>'
        + '<div class="flex items-center justify-between gap-3 rounded-xl px-3 py-3">'
        + '<div class="flex items-center gap-3"><span class="h-8 w-8 animate-pulse rounded-full bg-white/[0.08]"></span><span class="space-y-1"><span class="block h-3 w-20 animate-pulse rounded bg-white/[0.08]"></span><span class="block h-2.5 w-14 animate-pulse rounded bg-white/[0.05]"></span></span></div>'
        + '<span class="h-3 w-8 animate-pulse rounded bg-white/[0.06]"></span>'
        + '</div>'
        + '</div>';
      setExpanded(true);
      setActiveItem(-1);
    }

    function fetchJson(url, signal) {
      return fetch(url, {
        headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' },
        signal: signal,
      }).then(function (response) {
        if (!response.ok) throw new Error('Failed to load tag suggestions');
        return response.json();
      });
    }

    function renderRecentSearches(items) {
      if (!items.length) return '';

      return '<div class="px-2 pb-2">'
        + '<div class="flex items-center justify-between gap-3 px-2 pb-2 pt-1">'
        + '<div class="text-[11px] font-semibold uppercase tracking-[0.2em] text-white/30">Recent searches</div>'
        + '<button type="button" data-tags-search-clear-recent class="rounded-full border border-white/10 bg-white/[0.04] px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-white/48 transition hover:bg-white/[0.08] hover:text-white">Clear</button>'
        + '</div>'
        + '<div class="flex flex-wrap gap-2 px-2 pb-2">'
        + items.map(function (item) {
            var encoded = encodeURIComponent(item);
            var escaped = escapeHtml(item);
            return '<span class="inline-flex items-center gap-1.5 rounded-full border border-white/10 bg-white/[0.05] pr-1 text-xs font-medium text-white/68 transition hover:bg-white/[0.08] hover:text-white">'
              + '<a href="/tags?q=' + encoded + '" data-tags-search-item data-tags-search-surface="recent_search" data-tags-search-query="' + escaped + '" role="option" aria-selected="false" class="rounded-full px-3 py-1.5">' + escaped + '</a>'
              + '<button type="button" data-tags-search-remove-recent data-tags-search-query="' + escaped + '" class="inline-flex h-6 w-6 items-center justify-center rounded-full text-white/38 transition hover:bg-black/20 hover:text-white" aria-label="Remove recent search ' + escaped + '">'
              + '&times;'
              + '</button>'
              + '</span>';
          }).join('')
        + '</div>'
      + '</div>';
    }

    function renderRescueSuggestions(items, query) {
      if (!items.length) return '';

      return '<div class="border-t border-white/8 px-2 pt-3">'
        + '<div class="px-2 pb-2 text-[11px] font-semibold uppercase tracking-[0.2em] text-white/30">Try these instead</div>'
        + '<div class="grid gap-1.5 px-1 pb-2">'
        + items.map(function (item, index) {
            var name = escapeHtml(item.name || '');
            var slug = escapeHtml(item.slug || '');
          return '<a href="/tag/' + slug + '" data-tags-search-item data-tags-search-surface="rescue_suggestion" data-tags-search-tag="' + slug + '" data-tags-search-query="' + escapeHtml(query || '') + '" data-tags-search-position="' + (index + 1) + '" role="option" aria-selected="false" class="flex items-center justify-between gap-3 rounded-xl px-3 py-2.5 text-sm text-white/72 transition hover:bg-white/[0.06] hover:text-white">'
              + '<span class="min-w-0"><span class="block truncate font-medium text-white">' + name + '</span><span class="block truncate text-xs text-white/34">#' + slug + '</span></span>'
              + '<span class="shrink-0 text-xs text-white/30">Popular</span>'
              + '</a>';
          }).join('')
        + '</div>'
        + '<div class="px-2 pb-2"><a href="/tags" class="inline-flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-sky-200 transition hover:text-white">Browse full tags index</a></div>'
      + '</div>';
    }

    function renderNoMatchState(query, rescueItems) {
      title.textContent = 'No matching tags';
      results.setAttribute('aria-busy', 'false');
      results.innerHTML = '<div class="rounded-xl px-3 py-4 text-sm text-white/42">No direct matches for <span class="text-white">' + escapeHtml(query) + '</span>. Try a broader keyword or jump into one of these popular tags.</div>'
        + renderRescueSuggestions(rescueItems || [], query);
      setExpanded(true);
      setActiveItem(-1);
    }

    function renderItems(items, query) {
      clearResults();
      results.setAttribute('aria-busy', 'false');

      var recentSearches = !query ? readRecentSearches() : [];

      if (!items.length && recentSearches.length) {
        title.textContent = 'Recent searches';
        results.innerHTML = renderRecentSearches(recentSearches);
        setExpanded(true);
        setActiveItem(-1);
        return;
      }

      if (!items.length) {
        return;
      }

      title.textContent = query ? 'Matching tags' : (recentSearches.length ? 'Recent and popular' : 'Popular tags');

      if (recentSearches.length) {
        results.insertAdjacentHTML('beforeend', renderRecentSearches(recentSearches));
      }

      items.forEach(function (item, index) {
        var link = document.createElement('a');
        var itemName = escapeHtml(item.name || '');
        var itemSlug = escapeHtml(item.slug || '');
        var recentClicks = Number(item.recent_clicks || 0);
        var metricLabel = recentClicks > 0
          ? recentClicks.toLocaleString() + ' recent'
          : Number(item.usage_count || 0).toLocaleString() + ' uses';
        link.href = '/tag/' + item.slug;
        link.className = 'flex items-center justify-between gap-3 rounded-xl px-3 py-3 text-sm text-white/72 transition hover:bg-white/[0.06] hover:text-white';
        link.setAttribute('data-tags-search-item', '');
        link.setAttribute('data-tags-search-surface', 'search_suggestion');
        link.setAttribute('data-tags-search-tag', item.slug || '');
        link.setAttribute('data-tags-search-query', item.name || item.slug || '');
        link.setAttribute('data-tags-search-input', query || '');
        link.setAttribute('data-tags-search-position', String(index + 1));
        link.setAttribute('aria-selected', 'false');
        link.setAttribute('role', 'option');
        link.tabIndex = -1;
        link.innerHTML =
          '<span class="flex min-w-0 items-center gap-3">'
            + '<span class="inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/[0.06] text-xs text-sky-200">#</span>'
            + '<span class="min-w-0"><span class="block truncate font-medium text-white">' + itemName + '</span><span class="block truncate text-xs text-white/36">#' + itemSlug + '</span></span>'
          + '</span>'
          + '<span class="shrink-0 text-xs text-white/34">' + metricLabel + '</span>';
        results.appendChild(link);
      });

      setExpanded(true);
      setActiveItem(-1);
    }

    function fetchSuggestions(query) {
      if (abortController) abortController.abort();
      abortController = new AbortController();
      latestQuery = query;
      renderLoadingState(query);

      var params = new URLSearchParams();
      if (query) params.set('q', query);

      fetchJson(endpoint + (params.toString() ? ('?' + params.toString()) : ''), abortController.signal)
        .then(function (payload) {
          if (input.value.trim() !== latestQuery) return;
          var items = Array.isArray(payload.data) ? payload.data.slice(0, 8) : [];
          if (items.length || query === '') {
            renderItems(items, query);
            return;
          }

          var popularParams = new URLSearchParams();
          popularParams.set('limit', '4');

          return fetchJson(popularEndpoint + '?' + popularParams.toString(), abortController.signal)
            .then(function (popularPayload) {
              if (input.value.trim() !== latestQuery) return;
              renderNoMatchState(query, Array.isArray(popularPayload.data) ? popularPayload.data : []);
            });
        })
        .catch(function (error) {
          if (error && error.name === 'AbortError') return;
          hidePanel();
        });
    }

    input.addEventListener('focus', function () {
      fetchSuggestions(input.value.trim());
    });

    input.addEventListener('input', function () {
      var query = input.value.trim();
      window.clearTimeout(debounceTimer);
      debounceTimer = window.setTimeout(function () {
        fetchSuggestions(query);
      }, 180);
    });

    input.addEventListener('keydown', function (event) {
      var items = getItems();

      if ((event.key === 'ArrowDown' || event.key === 'ArrowUp') && items.length) {
        event.preventDefault();
        if (event.key === 'ArrowDown') {
          setActiveItem(activeIndex < 0 ? 0 : Math.min(activeIndex + 1, items.length - 1));
        } else {
          setActiveItem(activeIndex < 0 ? items.length - 1 : Math.max(activeIndex - 1, 0));
        }
        return;
      }

      if (event.key === 'Enter' && activeIndex >= 0 && items[activeIndex]) {
        event.preventDefault();
        writeRecentSearch(items[activeIndex].getAttribute('data-tags-search-query') || input.value);
        window.location.href = items[activeIndex].href;
        return;
      }

      if (event.key === 'Escape') {
        hidePanel();
      }
    });

    results.addEventListener('keydown', function (event) {
      var items = getItems();
      var currentIndex = items.indexOf(document.activeElement);

      if (event.key === 'ArrowDown' && items.length) {
        event.preventDefault();
        focusItem(currentIndex + 1);
        return;
      }

      if (event.key === 'ArrowUp' && items.length) {
        event.preventDefault();
        if (currentIndex <= 0) {
          setActiveItem(-1);
          input.focus();
        } else {
          focusItem(currentIndex - 1);
        }
        return;
      }

      if (event.key === 'Escape') {
        event.preventDefault();
        hidePanel();
        input.focus();
      }
    });

    results.addEventListener('mouseover', function (event) {
      var item = findClosest(event.target, '[data-tags-search-item]');
      if (!item) return;
      var items = getItems();
      setActiveItem(items.indexOf(item));
    });

    results.addEventListener('click', function (event) {
      var clearButton = findClosest(event.target, '[data-tags-search-clear-recent]');
      if (clearButton) {
        event.preventDefault();
        clearRecentSearches();
        fetchSuggestions(input.value.trim());
        return;
      }

      var removeButton = findClosest(event.target, '[data-tags-search-remove-recent]');
      if (removeButton) {
        event.preventDefault();
        removeRecentSearch(removeButton.getAttribute('data-tags-search-query') || '');
        fetchSuggestions(input.value.trim());
        return;
      }

      var item = findClosest(event.target, '[data-tags-search-item]');
      if (!item) return;
      var query = item.getAttribute('data-tags-search-query') || item.textContent || '';
      writeRecentSearch(query);

      var surface = item.getAttribute('data-tags-search-surface') || '';
      var tagSlug = item.getAttribute('data-tags-search-tag') || '';

      if (surface && (tagSlug || surface === 'recent_search')) {
        sendTagInteractionEvent({
          event_type: 'click',
          surface: surface,
          tag_slug: tagSlug || null,
          query: item.getAttribute('data-tags-search-input') || item.getAttribute('data-tags-search-query') || input.value.trim() || null,
          position: Number(item.getAttribute('data-tags-search-position') || 0) || null,
          occurred_at: new Date().toISOString(),
        });
      }
    });

    if (form) {
      form.addEventListener('submit', function () {
        writeRecentSearch(input.value);
      });
    }

    document.addEventListener('click', function (event) {
      if (!root.contains(event.target)) {
        hidePanel();
      }
    });
  });
}

initTagsSearchAssist();

function initTagAnalyticsLinks() {
  document.addEventListener('click', function (event) {
    var el = event.target;

    while (el && el.nodeType === 1) {
      if (el.matches('[data-tag-analytics-link]')) {
        var tagSlug = el.getAttribute('data-tag-analytics-tag') || '';
        var surface = el.getAttribute('data-tag-analytics-surface') || '';

        if (tagSlug && surface) {
          sendTagInteractionEvent({
            event_type: 'click',
            surface: surface,
            tag_slug: tagSlug,
            source_tag_slug: el.getAttribute('data-tag-analytics-source-tag') || null,
            position: Number(el.getAttribute('data-tag-analytics-position') || 0) || null,
            occurred_at: new Date().toISOString(),
          });
        }

        return;
      }

      el = el.parentElement;
    }
  });
}

initTagAnalyticsLinks();

(function () {
  function initBlurPreviewImages() {
    var selector = 'img[data-blur-preview]';

    function markLoaded(img) {
      if (!img) return;
      img.classList.remove('blur-sm', 'scale-[1.02]');
      img.classList.add('is-loaded');
    }

    document.querySelectorAll(selector).forEach(function (img) {
      if (img.complete && img.naturalWidth > 0) {
        markLoaded(img);
        return;
      }
      img.addEventListener('load', function () { markLoaded(img); }, { once: true });
      img.addEventListener('error', function () { markLoaded(img); }, { once: true });
    });

    document.addEventListener('load', function (event) {
      var target = event.target;
      if (target && target.matches && target.matches(selector)) {
        markLoaded(target);
      }
    }, true);
  }

  initBlurPreviewImages();

  function closest(el, selector) {
    while (el && el.nodeType === 1) {
      if (el.matches(selector)) return el;
      el = el.parentElement;
    }
    return null;
  }

  function canHover() {
    return window.matchMedia && window.matchMedia('(hover: hover) and (pointer: fine)').matches;
  }

  function setExpanded(toggleEl, expanded) {
    if (!toggleEl) return;
    toggleEl.setAttribute('aria-expanded', expanded ? 'true' : 'false');
  }

  function closeAllDropdowns(except) {
    var dropdowns = document.querySelectorAll('[data-dropdown]');
    dropdowns.forEach(function (dropdown) {
      if (except && dropdown === except) return;
      var menu = dropdown.querySelector('[data-dropdown-menu]');
      var toggle = dropdown.querySelector('[data-dropdown-toggle]');
      if (menu) menu.classList.remove('is-open');
      setExpanded(toggle, false);

      // Close any submenus
      dropdown.querySelectorAll('[data-submenu-menu]').forEach(function (sm) {
        sm.classList.add('hidden');
      });
      dropdown.querySelectorAll('[data-submenu-toggle]').forEach(function (st) {
        setExpanded(st, false);
      });
    });
  }

  function openDropdown(dropdown) {
    var menu = dropdown.querySelector('[data-dropdown-menu]');
    var toggle = dropdown.querySelector('[data-dropdown-toggle]');
    if (!menu || !toggle) return;
    closeAllDropdowns(dropdown);
    menu.classList.add('is-open');
    setExpanded(toggle, true);
  }

  function closeDropdown(dropdown) {
    var menu = dropdown.querySelector('[data-dropdown-menu]');
    var toggle = dropdown.querySelector('[data-dropdown-toggle]');
    if (menu) menu.classList.remove('is-open');
    setExpanded(toggle, false);
  }

  function toggleDropdown(dropdown) {
    var menu = dropdown.querySelector('[data-dropdown-menu]');
    var toggle = dropdown.querySelector('[data-dropdown-toggle]');
    if (!menu || !toggle) return;

    var isOpen = menu.classList.contains('is-open');
    closeAllDropdowns(isOpen ? null : dropdown);

    if (isOpen) {
      menu.classList.remove('is-open');
      setExpanded(toggle, false);
    } else {
      menu.classList.add('is-open');
      setExpanded(toggle, true);
    }
  }

  function getMobileMenu() {
    return document.getElementById('mobileMenu');
  }

  function setMobileToggleVisual(isOpen) {
    var toggle = document.querySelector('[data-mobile-toggle]') || document.getElementById('btnSidebar');
    if (!toggle) return;

    setExpanded(toggle, !!isOpen);

    var hamburgerIcon = toggle.querySelector('[data-mobile-icon-hamburger]');
    var closeIcon = toggle.querySelector('[data-mobile-icon-close]');
    if (hamburgerIcon) hamburgerIcon.classList.toggle('hidden', !!isOpen);
    if (closeIcon) closeIcon.classList.toggle('hidden', !isOpen);
  }

  function closeMobileMenu() {
    var menu = getMobileMenu();
    if (!menu) return;
    menu.classList.add('hidden');
    setMobileToggleVisual(false);
  }

  function toggleMobileMenu() {
    var menu = getMobileMenu();
    if (!menu) return;

    var isOpen = !menu.classList.contains('hidden');
    if (isOpen) {
      closeMobileMenu();
    } else {
      menu.classList.remove('hidden');
      setMobileToggleVisual(true);
      closeAllDropdowns();
    }
  }

  document.addEventListener('click', function (e) {
    var dropdownToggle = closest(e.target, '[data-dropdown-toggle]');
    // legacy shorthand toggles: data-dd="name" -> menu id = dd-name
    var legacyToggle = closest(e.target, '[data-dd]');
    if (dropdownToggle) {
      // On pointer/hover-capable devices prefer hover; ignore mouse clicks
      if (canHover() && e.detail > 0) {
        // allow keyboard activation (e.detail === 0) to fall through
        return;
      }
      e.preventDefault();
      var dropdown = closest(dropdownToggle, '[data-dropdown]');
      if (dropdown) toggleDropdown(dropdown);
      return;
    }

    if (legacyToggle) {
      // On pointer/hover-capable devices prefer hover; ignore mouse clicks
      if (canHover() && e.detail > 0) {
        return;
      }
      e.preventDefault();
      var ddName = legacyToggle.getAttribute('data-dd');
      if (!ddName) return;
      var menu = document.getElementById('dd-' + ddName);
      if (!menu) return;

      // treat this pair (toggle + menu) similarly to our dropdown API
      var isOpen = menu.classList.contains('is-open');
      // close other dropdowns
      closeAllDropdowns();
      // also close other legacy (data-dd) menus
      document.querySelectorAll('[data-dd]').forEach(function (other) {
        if (other === legacyToggle) return;
        var otherId = other.getAttribute('data-dd');
        var otherMenu = otherId ? document.getElementById('dd-' + otherId) : null;
        if (otherMenu) otherMenu.classList.remove('is-open');
        setExpanded(other, false);
      });

      if (isOpen) {
        menu.classList.remove('is-open');
        setExpanded(legacyToggle, false);
      } else {
        menu.classList.add('is-open');
        setExpanded(legacyToggle, true);
      }

      return;
    }

    var mobileToggle = closest(e.target, '[data-mobile-toggle]');
    if (mobileToggle) {
      e.preventDefault();
      toggleMobileMenu();
      return;
    }

    var mobileSectionToggle = closest(e.target, '[data-mobile-section-toggle]');
    if (mobileSectionToggle) {
      e.preventDefault();

      var panelId = mobileSectionToggle.getAttribute('aria-controls');
      var panel = panelId ? document.getElementById(panelId) : null;
      if (!panel) return;

      var wasOpen = !panel.classList.contains('hidden');
      var menuRoot = getMobileMenu();

      // Keep mobile navigation tidy: close all sections first.
      if (menuRoot) {
        menuRoot.querySelectorAll('[data-mobile-section-panel]').forEach(function (el) {
          el.classList.add('hidden');
        });
        menuRoot.querySelectorAll('[data-mobile-section-toggle]').forEach(function (btn) {
          setExpanded(btn, false);
          var icon = btn.querySelector('[data-mobile-section-icon]');
          if (icon) icon.classList.remove('rotate-180');
        });
      }

      // If it was closed, open it. If it was open, it stays closed (toggle behavior).
      if (!wasOpen) {
        panel.classList.remove('hidden');
        setExpanded(mobileSectionToggle, true);
        var currentIcon = mobileSectionToggle.querySelector('[data-mobile-section-icon]');
        if (currentIcon) currentIcon.classList.add('rotate-180');
      }

      return;
    }

    // Submenu toggle (touch/click fallback)
    var submenuToggle = closest(e.target, '[data-submenu-toggle]');
    if (submenuToggle) {
      if (canHover()) {
        // On desktop, submenu opens on hover via CSS.
        e.preventDefault();
        return;
      }

      e.preventDefault();
      var submenu = closest(submenuToggle, '[data-submenu]');
      if (!submenu) return;
      var menu = submenu.querySelector('[data-submenu-menu]');
      if (!menu) return;

      // Close other submenus within the same dropdown
      var dropdown = closest(submenuToggle, '[data-dropdown]');
      if (dropdown) {
        dropdown.querySelectorAll('[data-submenu-menu]').forEach(function (sm) {
          if (sm !== menu) sm.classList.add('hidden');
        });
        dropdown.querySelectorAll('[data-submenu-toggle]').forEach(function (st) {
          if (st !== submenuToggle) setExpanded(st, false);
        });
      }

      var isOpen = !menu.classList.contains('hidden');
      if (isOpen) {
        menu.classList.add('hidden');
        setExpanded(submenuToggle, false);
      } else {
        menu.classList.remove('hidden');
        setExpanded(submenuToggle, true);
      }

      return;
    }

    if (!closest(e.target, '[data-dropdown]')) {
      closeAllDropdowns();
    }

    // Close mobile menu when tapping outside of it and outside the hamburger toggle.
    var mobileMenu = getMobileMenu();
    var mobileToggle = closest(e.target, '[data-mobile-toggle]') || closest(e.target, '#btnSidebar');
    if (mobileMenu && !mobileMenu.classList.contains('hidden') && !mobileToggle && !closest(e.target, '#mobileMenu')) {
      closeMobileMenu();
    }
  });

  // Hover-to-open for desktop pointers
  var hoverCloseTimers = new WeakMap();
  function clearHoverTimer(dropdown) {
    var t = hoverCloseTimers.get(dropdown);
    if (t) window.clearTimeout(t);
    hoverCloseTimers.delete(dropdown);
  }

  function scheduleClose(dropdown) {
    clearHoverTimer(dropdown);
    hoverCloseTimers.set(
      dropdown,
      window.setTimeout(function () {
        closeMenuElement(dropdown);
      }, 140)
    );
  }

  // Close a menu element or its parent dropdown wrapper.
  function closeMenuElement(el) {
    if (!el) return;

    // If this is a dropdown wrapper, find the menu inside
    if (el.hasAttribute && el.hasAttribute('data-dropdown')) {
      var menu = el.querySelector('[data-dropdown-menu]');
      var toggle = el.querySelector('[data-dropdown-toggle]');
      if (menu) menu.classList.remove('is-open');
      setExpanded(toggle, false);
      // also close submenus inside
      el.querySelectorAll('[data-submenu-menu]').forEach(function (sm) {
        sm.classList.add('hidden');
      });
      el.querySelectorAll('[data-submenu-toggle]').forEach(function (st) {
        setExpanded(st, false);
      });
      return;
    }

    // If it's a menu element (e.g., legacy id=dd-name) hide it and try to find its toggle
    var menuEl = el;
    if (!menuEl.id && el.getAttribute && el.getAttribute('data-dropdown-menu')) {
      // explicit menu element
    }

    // hide the element if possible
    try { menuEl.classList.remove('is-open'); } catch (e) {}

    // Try to map back to a toggle: id like dd-name -> data-dd="name"
    if (menuEl.id && menuEl.id.indexOf('dd-') === 0) {
      var name = menuEl.id.slice(3);
      var toggle = document.querySelector('[data-dd="' + name + '"]');
      if (toggle) setExpanded(toggle, false);
    } else {
      // fallback: if menu is inside a [data-dropdown], handled above; nothing more to do
    }
  }

  function bindHoverHandlers() {
    if (!canHover()) return;
    document.querySelectorAll('[data-dropdown]').forEach(function (dropdown) {
      dropdown.addEventListener('mouseenter', function () {
        clearHoverTimer(dropdown);
        openDropdown(dropdown);
      });
      dropdown.addEventListener('mouseleave', function () {
        scheduleClose(dropdown);
      });
    });

    // legacy hover binding for shorthand toggles (data-dd)
    document.querySelectorAll('[data-dd]').forEach(function (el) {
      var ddName = el.getAttribute('data-dd');
      if (!ddName) return;
      var menu = document.getElementById('dd-' + ddName);
      if (!menu) return;

      // when pointer enters either toggle or menu, open
      function enter() {
        clearHoverTimer(menu);
        // Instantly close any other open legacy dropdown to prevent overlap
        document.querySelectorAll('[data-dd]').forEach(function (other) {
          if (other === el) return;
          var otherId = other.getAttribute('data-dd');
          var otherMenu = otherId ? document.getElementById('dd-' + otherId) : null;
          if (otherMenu) otherMenu.classList.remove('is-open');
          setExpanded(other, false);
        });
        menu.classList.add('is-open');
        setExpanded(el, true);
      }

      function leave() {
        scheduleClose(menu);
      }

      el.addEventListener('mouseenter', enter);
      el.addEventListener('mouseleave', leave);
      menu.addEventListener('mouseenter', enter);
      menu.addEventListener('mouseleave', leave);
    });
  }

  bindHoverHandlers();

  // Submenu hover handlers: ensure flyouts open on pointer devices
  if (canHover()) {
    document.querySelectorAll('[data-submenu]').forEach(function (group) {
      var toggle = group.querySelector('[data-submenu-toggle]');
      var menu = group.querySelector('[data-submenu-menu]');
      if (!menu) return;

      group.addEventListener('mouseenter', function () {
        menu.classList.remove('hidden');
        if (toggle) setExpanded(toggle, true);
      });
      group.addEventListener('mouseleave', function () {
        menu.classList.add('hidden');
        if (toggle) setExpanded(toggle, false);
      });
    });
  }

  document.addEventListener('keydown', function (e) {
    if (e.key !== 'Escape') return;
    closeAllDropdowns();
    closeMobileMenu();
  });

  window.addEventListener('resize', function () {
    if (window.matchMedia('(min-width: 768px)').matches) {
      closeMobileMenu();
    }
  });
})();
