import { apiFetch, basePath, debounce, escapeHtml } from 'ect/site';
import { identitySummaryMarkup, manaCostMarkup, typeLineMarkup } from 'ect/mana';
import { comboInputs, filterComboOptions } from './multi-combo.js';
import { clearViewportPanel, createActivationTracker, observeViewport, positionViewportPanel } from 'ect/viewport-ui';

const app = document.querySelector('#deck-search-app');
if (!app) throw new Error('Deck search root is missing.');

const cardBackImage = `${basePath}/assets/images/ect-card-back.png`;
const imageFallbackAttr = ` onerror="this.onerror=null;this.src='${escapeHtml(cardBackImage)}';"`;
const strategyLabels = (() => { try { return JSON.parse(app?.dataset.strategyLabels || '{}') || {}; } catch { return {}; } })();
const archetypeLabels = (() => { try { return JSON.parse(app?.dataset.archetypeLabels || '{}') || {}; } catch { return {}; } })();
const form = document.querySelector('#deck-search-form');
const results = document.querySelector('#deck-results');
const summary = document.querySelector('#deck-result-summary');
const pagination = document.querySelector('#deck-pagination');
const sentinel = document.querySelector('#deck-results-sentinel');
const multiCombos = [...form.querySelectorAll('[data-filter-name]')];
const subtypeCombo = document.getElementById('filter-subtype');
const autocompleteCombos = [...form.querySelectorAll('[data-deck-search-combobox]')];
const breakDoubleFaceName = (value) => escapeHtml(String(value || '')).replace(/\s*\/\/\s*/g, '<br>// ');

let page = 1;
let autocompleteController = null;
let requestController = null;
let totalPages = 1;
let totalResults = 0;
let loadingNextPage = false;
let autoPagingPaused = false;
let activeLoadToken = 0;
let pagingObserver = null;

function updateComboSummary(combo) {
  const selected = comboInputs(combo).filter((input) => input.checked);
  const node = combo.querySelector('[data-multi-summary]');
  if (!node) return;
  if (!selected.length) node.textContent = combo.dataset.placeholder || 'Any';
  else if (selected.length === 1) {
    const labelNode = selected[0].closest('label')?.querySelector('[data-option-label]');
    if (labelNode?.querySelector('.card-type-line,.mana-option-label')) node.innerHTML = labelNode.innerHTML;
    else node.textContent = labelNode?.dataset.optionLabel || labelNode?.textContent || selected[0].value;
  } else if (selected.length === comboInputs(combo).length) node.textContent = `All ${selected.length} selected`;
  else node.textContent = `${selected.length} selected`;
}
function setMultiFilter(name, values) {
  const combo = multiCombos.find((candidate) => candidate.dataset.filterName === name);
  if (!combo) return;
  const wanted = new Set(values.map(String));
  comboInputs(combo).forEach((input) => { input.checked = wanted.has(input.value); });
  updateComboSummary(combo);
}
function initMultiCombos() {
  multiCombos.forEach((combo) => {
    const trigger = combo.querySelector('summary');
    const activation = createActivationTracker(trigger);
    updateComboSummary(combo);
    combo.querySelector('[data-multi-select-all]')?.addEventListener('click', () => {
      comboInputs(combo).filter((input) => !input.closest('label')?.hidden).forEach((input) => { input.checked = true; });
      updateComboSummary(combo);
      form.dispatchEvent(new Event('change', { bubbles: true }));
    });
    combo.querySelector('[data-multi-clear]')?.addEventListener('click', () => {
      comboInputs(combo).forEach((input) => { input.checked = false; });
      updateComboSummary(combo);
      form.dispatchEvent(new Event('change', { bubbles: true }));
    });
    combo.querySelector('[data-multi-search]')?.addEventListener('input', (event) => filterComboOptions(combo, event.target.value));
    combo.addEventListener('change', () => updateComboSummary(combo));
    combo.addEventListener('toggle', () => {
      const panel = combo.querySelector('.multi-combo-panel');
      if (!combo.open) { clearViewportPanel(panel); return; }
      multiCombos.forEach((candidate) => { if (candidate !== combo) candidate.open = false; });
      closeAutocompleteCombos();
      requestAnimationFrame(() => {
        positionViewportPanel(panel, trigger);
        if (activation.isKeyboard()) combo.querySelector('[data-multi-search]')?.focus({ preventScroll: true });
      });
    });
  });
  observeViewport(() => multiCombos.filter((combo) => combo.open).forEach((combo) => {
    positionViewportPanel(combo.querySelector('.multi-combo-panel'), combo.querySelector('summary'));
  }));
  document.addEventListener('click', (event) => {
    if (!event.target.closest('.multi-combo')) multiCombos.forEach((combo) => { combo.open = false; });
  });
}

function closeAutocompleteCombos(except = null) {
  autocompleteCombos.forEach((root) => {
    if (root === except) return;
    root.classList.remove('is-open');
    root.querySelector('[role="listbox"]')?.setAttribute('hidden', '');
    root.querySelector('[role="combobox"]')?.setAttribute('aria-expanded', 'false');
  });
}
function autocompleteParts(root) {
  return { input: root.querySelector('[role="combobox"]'), options: root.querySelector('[role="listbox"]') };
}
function cardOptionMarkup(item) {
  const image = `<img class="search-combobox-card-image" src="${escapeHtml(item.image || cardBackImage)}" alt="" loading="lazy" decoding="async"${imageFallbackAttr}>`;
  const detail = `<small><span class="search-combobox-card-mana">${manaCostMarkup(item.manaCost || '')}</span><span class="search-combobox-card-type">${typeLineMarkup(item.meta || '', { className: 'card-type-line-compact' })}</span></small>`;
  return `${image}<span><strong>${breakDoubleFaceName(item.label)}</strong>${detail}</span>`;
}
function brewerOptionMarkup(item) {
  const initial = String(item.nickname || item.label || '?').trim().slice(0, 1).toUpperCase() || '?';
  const detail = item.nickname && item.nickname !== item.label ? `<small>${escapeHtml(item.nickname)}</small>` : (item.meta ? `<small>${escapeHtml(item.meta)}</small>` : '');
  const image = profileImageUrl(item);
  const icon = image ? `<span class="deck-search-brewer-initial user-avatar"><img src="${escapeHtml(image)}" alt=""></span>` : `<span class="deck-search-brewer-initial" aria-hidden="true">${escapeHtml(initial)}</span>`;
  return `${icon}<span><strong>${escapeHtml(item.label)}</strong>${detail}</span>`;
}
function renderAutocompleteOptions(root, items) {
  const { input, options } = autocompleteParts(root);
  if (!input || !options) return;
  const kind = root.dataset.deckSearchCombobox || '';
  options.innerHTML = '';
  items.forEach((item, index) => {
    const button = document.createElement('button');
    button.type = 'button';
    button.className = `search-combobox-option ${kind === 'brewer' ? 'deck-search-brewer-option' : 'search-combobox-option-card'}`;
    button.role = 'option';
    button.dataset.value = String(item.value || item.label || '');
    button.dataset.id = String(item.id || '');
    button.dataset.label = String(item.label || item.value || '');
    button.dataset.index = String(index);
    button.setAttribute('aria-selected', String(index === 0));
    button.innerHTML = kind === 'brewer' ? brewerOptionMarkup(item) : cardOptionMarkup(item);
    options.append(button);
  });
  options.hidden = items.length === 0;
  root.classList.toggle('is-open', items.length > 0);
  input.setAttribute('aria-expanded', String(items.length > 0));
}
async function searchAutocomplete(root, query) {
  const kind = root.dataset.deckSearchCombobox || '';
  const term = query.trim();
  if (term.length < 2) {
    renderAutocompleteOptions(root, []);
    return;
  }
  autocompleteController?.abort();
  autocompleteController = new AbortController();
  try {
    if (kind === 'brewer') {
      const params = new URLSearchParams({ q: term, pageSize: '12' });
      const payload = await apiFetch(`/api/v1/users/search?${params}`, { signal: autocompleteController.signal });
      const items = (payload.data.items || []).map((user) => ({ value: user.username, label: user.username, nickname: user.nickname || '', profileImage: user.profileImage || '', meta: user.publicDecks ? `${user.publicDecks} public deck${user.publicDecks === 1 ? '' : 's'}` : 'Public brewer' }));
      renderAutocompleteOptions(root, items);
      return;
    }
    const params = new URLSearchParams({ q: term, nameOnly: '1', pageSize: '20', sort: 'relevance', paperOnly: '1' });
    if (kind === 'leader') {
      params.append('eligibility[]', 'leader');
      params.set('eligibleOnly', '1');
    }
    const payload = await apiFetch(`/api/v1/cards/search?${params}`, { signal: autocompleteController.signal });
    const items = (payload.data.items || []).map((card) => ({
      id: card.oracleId || '',
      value: card.name,
      label: card.name,
      meta: card.typeLine || card.typeLineFront || '',
      manaCost: card.manaCost || '',
      image: card.defaultPrinting?.imageSmall || card.defaultPrinting?.imageNormal || card.imageSmall || '',
    }));
    renderAutocompleteOptions(root, items);
  } catch (error) {
    if (error?.name !== 'AbortError') renderAutocompleteOptions(root, []);
  }
}
function highlightAutocomplete(root, delta) {
  const buttons = [...root.querySelectorAll('.search-combobox-option')];
  if (!buttons.length) return;
  const current = buttons.findIndex((button) => button.getAttribute('aria-selected') === 'true');
  const next = (current + delta + buttons.length) % buttons.length;
  buttons.forEach((button, index) => button.setAttribute('aria-selected', String(index === next)));
  buttons[next].scrollIntoView({ block: 'nearest' });
}
function chooseAutocomplete(root, button) {
  const { input } = autocompleteParts(root);
  if (!input || !button) return;
  input.value = button.dataset.value || button.dataset.label || '';
  const idInput = root.querySelector('input[type="hidden"]');
  if (idInput) idInput.value = button.dataset.id || '';
  closeAutocompleteCombos();
  form.dispatchEvent(new Event('change', { bubbles: true }));
}
function initAutocompleteCombos() {
  autocompleteCombos.forEach((root) => {
    const { input, options } = autocompleteParts(root);
    if (!input || !options) return;
    const search = debounce(() => searchAutocomplete(root, input.value), 220);
    input.addEventListener('focus', () => { closeAutocompleteCombos(root); if (input.value.trim().length >= 2) searchAutocomplete(root, input.value); });
    input.addEventListener('input', () => { const idInput = root.querySelector('input[type="hidden"]'); if (idInput) idInput.value = ''; closeAutocompleteCombos(root); search(); });
    input.addEventListener('keydown', (event) => {
      if (event.key === 'ArrowDown') { event.preventDefault(); highlightAutocomplete(root, 1); }
      else if (event.key === 'ArrowUp') { event.preventDefault(); highlightAutocomplete(root, -1); }
      else if ((event.key === 'Enter' || event.key === 'Tab') && !options.hidden) {
        const active = root.querySelector('.search-combobox-option[aria-selected="true"]') || root.querySelector('.search-combobox-option');
        if (active) { event.preventDefault(); chooseAutocomplete(root, active); }
      } else if (event.key === 'Escape') closeAutocompleteCombos();
    });
    options.addEventListener('mousedown', (event) => {
      const button = event.target.closest('.search-combobox-option');
      if (!button) return;
      event.preventDefault();
      chooseAutocomplete(root, button);
    });
  });
  document.addEventListener('click', (event) => {
    if (!event.target.closest('[data-deck-search-combobox]')) closeAutocompleteCombos();
  });
}

const read = (requestedPage = page) => {
  const p = new URLSearchParams();
  const data = new FormData(form);
  for (const [key, value] of data.entries()) {
    const text = String(value).trim();
    if (text !== '') p.append(key, text);
  }
  p.set('page', String(requestedPage));
  if (!p.get('pageSize')) p.set('pageSize', '24');
  return p;
};
const validationClass = (value) => {
  const status = String(value || 'unchecked').toLowerCase();
  return ['legal', 'legal_with_warnings'].includes(status) ? 'is-validated' : 'is-unvalidated';
};
const strategyLabel = (value) => {
  const key = String(value || '').toLowerCase().trim();
  if (!key || ['none', 'no_strategy', 'no-strategy'].includes(key)) return '';
  const label = strategyLabels[key] || key.replaceAll('_', ' ').replaceAll('-', ' ').replace(/\b\w/g, (char) => char.toUpperCase());
  return /^no\s+strategy$/i.test(label) ? '' : label;
};
const subtypesMarkup = (subtypes) => {
  const list = Array.isArray(subtypes) ? subtypes.filter(Boolean).slice(0, 4) : [];
  return list.length ? list.map((tag) => `<em>${escapeHtml(tag)}</em>`).join('') : '';
};
const profileUrl = (d) => d.profileUrl || d.profile_url || (d.username ? `${basePath}/users/${encodeURIComponent(d.username)}` : '');
const profileImageUrl = (d) => {
  const value = String(d.profileImagePath || d.profile_image_path || d.profileImage || d.profile_image || '').trim();
  if (!value) return '';
  if (/^https?:\/\//i.test(value)) return value;
  return `${basePath}/${value.replace(/^\/+/, '')}`;
};
const ownerBadge = (d) => {
  const nickname = String(d.profileNickname || d.profile_nickname || d.nickname || d.displayName || d.username || 'Unknown brewer');
  const initial = (nickname.trim()[0] || '?').toUpperCase();
  const url = profileUrl(d);
  const image = profileImageUrl(d);
  const deckCount = Number(d.ownerPublicDeckCount || d.owner_public_deck_count || d.deckCount || 0);
  const aria = `${nickname} · Deck Count ${deckCount}${url ? ' · Click to view profile' : ''}`;
  const icon = image
    ? `<span class="deck-view-card-owner-icon user-avatar"><img src="${escapeHtml(image)}" alt="" loading="lazy" decoding="async"></span>`
    : `<span class="deck-view-card-owner-icon" aria-hidden="true">${escapeHtml(initial)}</span>`;
  const popover = `<strong>${escapeHtml(nickname)}</strong><span>Deck Count ${deckCount.toLocaleString()}</span>${url ? '<span>Click to view profile</span>' : ''}`;
  return `<span class="deck-view-card-owner deck-view-card-brewer" ${url ? `role="link" tabindex="0" data-profile-url="${escapeHtml(url)}"` : ''} aria-label="${escapeHtml(aria)}">${icon}<span class="deck-view-card-owner-popover" role="tooltip">${popover}</span></span>`;
};
function card(d) {
  const leader = String(d.leaderName || 'No Leader selected');
  const identity = identitySummaryMarkup(d.leaderColorIdentity || '', d.bonusColor || '');
  const image = `<img src="${escapeHtml(d.leaderImage || cardBackImage)}" alt="${escapeHtml(d.leaderImage ? `${leader} card` : 'Card back')}" loading="lazy" decoding="async"${imageFallbackAttr}>`;
  const subtypes = subtypesMarkup(d.leaderSubtypes);
  const strategy = strategyLabel(d.deckStrategy);
  const archetype = archetypeLabels[String(d.deckArchetype || '').toLowerCase().trim()] || '';
  const facts = [
    identity ? `<div class="deck-view-card-fact deck-view-card-colors" aria-label="Leader colors and additional color">${identity}</div>` : '',
    subtypes ? `<div class="deck-view-card-fact deck-view-card-subtypes" aria-label="Leader subtypes">${subtypes}</div>` : '',
    strategy ? `<div class="deck-view-card-fact deck-view-card-strategy" aria-label="Deck classification"><span class="deck-view-card-strategy-text">${escapeHtml(strategy)}${archetype ? ` · ${escapeHtml(archetype)}` : ''}</span></div>` : '',
  ].join('');
  const matches = Number(d.localMatches ?? d.localRecord?.matches ?? 0);
  const winRateValue = d.localWinRate ?? d.localRecord?.winRate;
  const winRate = winRateValue === null || winRateValue === undefined || !Number.isFinite(Number(winRateValue)) ? null : Number(winRateValue);
  const spicinessValue = d.spicinessScore ?? d.spiciness?.score;
  const spiciness = spicinessValue === null || spicinessValue === undefined || !Number.isFinite(Number(spicinessValue)) ? null : Number(spicinessValue);
  const spicinessLabel = String(d.spicinessLabel || d.spiciness?.label || 'Not enough data');
  const peerDecks = Number(d.spicinessPeerDecks ?? d.spiciness?.peerDecks ?? 0);
  const metrics = `<div class="deck-discovery-metrics" aria-label="Deck performance and originality"><span class="deck-discovery-metric" title="${escapeHtml(matches > 0 ? `${matches.toLocaleString()} local match${matches === 1 ? '' : 'es'}` : 'No completed local matches')}"><small>Local win rate</small><strong>${winRate === null ? '—' : `${winRate.toFixed(1)}%`}</strong><em>${matches > 0 ? `${matches.toLocaleString()} match${matches === 1 ? '' : 'es'}` : 'No data'}</em></span><span class="deck-discovery-metric" title="${escapeHtml(spiciness === null ? 'Not enough comparable decks' : `Compared with ${peerDecks.toLocaleString()} similar deck${peerDecks === 1 ? '' : 's'}`)}"><small>Spiciness</small><strong>${spiciness === null ? '—' : spiciness.toFixed(1)}</strong><em>${escapeHtml(spicinessLabel)}</em></span></div>`;
  return `<a class="deck-view-card deck-result-card deck-result-card-leader deck-index-card ${validationClass(d.legality)}" href="${basePath}/decks/${encodeURIComponent(d.urlKey)}"><div class="deck-view-card-body"><div class="deck-view-card-primary"><div class="deck-view-card-title-row"><strong class="deck-view-card-name">${escapeHtml(d.name || 'Untitled deck')}</strong>${ownerBadge(d)}</div><span class="deck-view-card-leader">${breakDoubleFaceName(leader)}</span></div><div class="deck-view-card-detail"><span class="deck-view-card-art">${image}</span><div class="deck-view-card-facts">${facts}${metrics}</div></div></div></a>`;
}
function hasNextPage() {
  return !autoPagingPaused && page < totalPages;
}
function updateResultSummary() {
  const showing = results.querySelectorAll('.deck-result-card').length;
  const pageCopy = totalPages > 1 ? ` · Page ${page.toLocaleString()} of ${totalPages.toLocaleString()}` : '';
  summary.textContent = `${totalResults.toLocaleString()} public deck${totalResults === 1 ? '' : 's'}${pageCopy} · showing ${showing.toLocaleString()}`;
}
function updateSentinelState(isLoading = false) {
  if (!sentinel) return;
  sentinel.hidden = !(isLoading || hasNextPage());
  sentinel.toggleAttribute('data-loading', isLoading);
  sentinel.textContent = isLoading ? 'Loading next page…' : '';
}
function pages(current, count) {
  pagination.replaceChildren();
  if (count <= 1) return;
  for (const n of new Set([1, current - 1, current, current + 1, count])) {
    if (n < 1 || n > count) continue;
    const b = document.createElement('button');
    b.type = 'button';
    b.textContent = n;
    if (n === current) b.setAttribute('aria-current', 'page');
    b.onclick = () => {
      load(n);
      document.querySelector('.deck-directory-summary')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
    };
    pagination.append(b);
  }
}
async function load(requestedPage = 1, { append = false } = {}) {
  if (append && (loadingNextPage || !hasNextPage())) return;
  if (!append) {
    requestController?.abort();
    loadingNextPage = false;
    autoPagingPaused = false;
    summary.textContent = 'Searching decks…';
    results.innerHTML = '<div class="loading-grid"><div class="skeleton-card"></div><div class="skeleton-card"></div></div>';
    updateSentinelState();
  } else {
    loadingNextPage = true;
    updateSentinelState(true);
  }
  const token = append ? activeLoadToken : activeLoadToken + 1;
  if (!append) activeLoadToken = token;
  const controller = new AbortController();
  requestController = controller;
  try {
    const p = read(requestedPage);
    const payload = await apiFetch(`/api/v1/decks/search?${p}`, { signal: controller.signal });
    if (token !== activeLoadToken) return;
    const d = payload.data;
    const items = Array.isArray(d.items) ? d.items : [];
    page = Number(d.page || requestedPage);
    totalPages = Number(d.pages || 1);
    totalResults = Number(d.total || 0);
    autoPagingPaused = append && items.length === 0;
    const markup = items.map(card).join('');
    if (append) results.insertAdjacentHTML('beforeend', markup);
    else results.innerHTML = markup || '<div class="empty-state"><h2>No decks found</h2><p>Remove a filter or publish the first matching deck.</p></div>';
    updateResultSummary();
    pages(page, totalPages);
    history.replaceState(null, '', `${location.pathname}${p.toString() ? `?${p}` : ''}`);
  } catch (e) {
    if (e?.name === 'AbortError' || token !== activeLoadToken) return;
    if (append) {
      autoPagingPaused = true;
      summary.textContent = `Next page failed: ${e.message}`;
    } else {
      summary.textContent = 'Deck search failed';
      results.innerHTML = `<div class="notice danger">${escapeHtml(e.message)}</div>`;
    }
  } finally {
    if (requestController === controller) requestController = null;
    if (token === activeLoadToken) {
      loadingNextPage = false;
      updateSentinelState();
    }
  }
}
function loadNextPage() {
  if (!hasNextPage() || loadingNextPage) return;
  load(page + 1, { append: true });
}
function initAutoPagination() {
  if (!sentinel || !('IntersectionObserver' in window)) return;
  pagingObserver?.disconnect();
  pagingObserver = new IntersectionObserver((entries) => {
    if (entries.some((entry) => entry.isIntersecting)) loadNextPage();
  }, { rootMargin: '900px 0px' });
  pagingObserver.observe(sentinel);
}
function restoreState() {
  const params = new URLSearchParams(location.search);
  if (!params.has('legality') && !params.has('legality[]') && params.has('status')) {
    const legacy = params.get('status') || '';
    params.set('legality', ({ valid: 'legal', valid_with_warnings: 'legal_with_warnings' })[legacy] || legacy);
  }
  for (const combo of multiCombos) {
    const name = combo.dataset.filterName;
    const values = [...params.getAll(`${name}[]`), ...params.getAll(name)];
    if (values.length) setMultiFilter(name, values);
  }
  for (const [k, v] of params) {
    if (k.endsWith('[]') || k === 'page') continue;
    const f = form.elements.namedItem(k);
    if (f && !(f instanceof RadioNodeList)) f.value = v;
  }
  page = Math.max(1, Number(params.get('page') || 1));
}
async function loadSubtypeFacets() {
  if (!subtypeCombo) return;
  try {
    const payload = await apiFetch('/api/v1/cards/facets');
    if (!payload.data.catalogReady) return;
    const options = subtypeCombo.querySelector('[data-multi-options]');
    if (!options || options.childElementCount > 0) return;
    const params = new URLSearchParams(location.search);
    const selected = new Set([...params.getAll('subtype[]'), ...params.getAll('subtype')]);
    const fragment = document.createDocumentFragment();
    for (const item of payload.data.subtypes || []) {
      const label = document.createElement('label');
      const input = document.createElement('input');
      input.type = 'checkbox';
      input.name = 'subtype[]';
      input.value = item.subtype;
      input.checked = selected.has(item.subtype);
      const text = document.createElement('span');
      text.dataset.optionLabel = item.subtype;
      text.textContent = `${item.subtype} (${Number(item.card_count).toLocaleString()})`;
      label.append(input, text);
      fragment.append(label);
    }
    options.append(fragment);
    updateComboSummary(subtypeCombo);
  } catch {}
}

form.addEventListener('submit', (event) => { event.preventDefault(); load(1); });
form.addEventListener('change', () => { load(1); });
form.q.addEventListener('input', debounce(() => { load(1); }, 400));
initMultiCombos();
initAutocompleteCombos();
await loadSubtypeFacets();
restoreState();
multiCombos.forEach(updateComboSummary);
await load(page);
initAutoPagination();
