async function fetchJson(url, options) {
  const res = await fetch(url, options);
  const payload = await res.json();
  if (!res.ok) throw new Error(payload.detail || 'Request failed for ' + url);
  return payload;
}

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

function setStatusMessage(message, tone = 'muted') {
  const status = document.getElementById('search-status');
  if (!status) return;
  status.textContent = message || '';
  status.className = tone;
}


function renderSuggestionCards(target, items) {
  if (!target) return;
  if (!items.length) {
    target.textContent = 'Nothing loaded yet.';
    target.classList.add('muted');
    return;
  }
  target.classList.remove('muted');
  target.innerHTML = items.map((item) => {
    const ingredients = (item.ingredients || []).slice(0, 8).map((entry) => '<span>' + escapeHtml(entry) + '</span>').join('');
    const reasons = (item.score_reasons || []).slice(0, 4).map((entry) => '<li>' + escapeHtml(entry) + '</li>').join('');
    const kitchenowl = item.kitchenowl_status && item.kitchenowl_status !== 'not-imported'
      ? '<p class="pill pill-good">KitchenOwl: ' + escapeHtml(item.kitchenowl_status) + '</p>'
      : '';
    const canFlagImport = ['created', 'updated', 'duplicate'].includes(String(item.kitchenowl_status || ''));
    const flagButton = canFlagImport
      ? '<button type="button" class="ghost" data-flag-suggestion="' + escapeHtml(item.id) + '">Flag import issue</button>'
      : '';
    return `
      <article class="suggestion-card" data-suggestion-id="${escapeHtml(item.id)}">
        <div class="suggestion-top">
          <div>
            <p class="score-pill">Score ${escapeHtml(item.score)}</p>
            <h3><a href="${escapeHtml(item.url)}" target="_blank" rel="noreferrer">${escapeHtml(item.title)}</a></h3>
            <p class="muted">${escapeHtml(item.source_query || item.source_title || '')}</p>
          </div>
          <p class="status-pill">${escapeHtml(item.status || 'suggested')}</p>
        </div>
        ${kitchenowl}
        <div class="chip-list">${ingredients || '<span>No ingredient list parsed</span>'}</div>
        <ul class="reason-list">${reasons}</ul>
        <div class="action-row">
          <button type="button" data-decision="approve">Yes, add to KitchenOwl</button>
          <button type="button" class="ghost" data-decision="later">Save for later</button>
          <button type="button" class="ghost danger" data-decision="reject">No thanks</button>
          ${flagButton}
        </div>
      </article>
    `;
  }).join('');
}

function renderFailureCards(target, items) {
  if (!target) return;
  if (!items.length) {
    target.textContent = 'No failed searches logged.';
    target.classList.add('muted');
    return;
  }
  target.classList.remove('muted');
  target.innerHTML = items.map((item) => {
    const status = String(item.status || 'open');
    const resolved = status === 'resolved-with-recipes';
    const titles = (item.sample_titles || []).map((entry) => '<li>' + escapeHtml(entry) + '</li>').join('');
    const candidateQueries = (item.candidate_queries || []).map((entry) => '<span>' + escapeHtml(entry) + '</span>').join('');
    return `
      <article class="failure-card" data-failure-id="${escapeHtml(item.id)}">
        <div class="suggestion-top">
          <div>
            <p class="section-label">Logged query</p>
            <h3>${escapeHtml(item.query)}</h3>
          </div>
          <p class="pill ${resolved ? 'status-resolved' : 'status-open'}">${escapeHtml(status)}</p>
        </div>
        <p class="muted">Logged: ${escapeHtml(item.created_at || '')}</p>
        <div class="chip-list">${candidateQueries || '<span>No candidate queries captured</span>'}</div>
        <p class="muted failure-count">Current matching recipes: ${escapeHtml(item.result_count || 0)}</p>
        <ul class="reason-list failure-title-list">${titles || '<li>No recovered recipe titles yet.</li>'}</ul>
        <div class="action-row">
          <button type="button" class="ghost" data-failure-open="${escapeHtml(item.id)}">View recipes</button>
        </div>
        <div class="failure-results stack-form muted" data-failure-results="${escapeHtml(item.id)}">Click <strong>View recipes</strong> to load the current recipe matches for this search.</div>
      </article>
    `;
  }).join('');
}

function renderImportFlagCards(target, items) {
  if (!target) return;
  if (!items.length) {
    target.textContent = 'No import issues flagged yet.';
    target.classList.add('muted');
    return;
  }
  target.classList.remove('muted');
  target.innerHTML = items.map((item) => {
    const status = String(item.status || 'open');
    const resolved = status === 'resolved';
    const history = (item.notes_history || []).slice(-3).reverse().map((entry) => '<li><strong>' + escapeHtml(entry.issue_type || 'issue') + ':</strong> ' + escapeHtml(entry.notes || '') + '</li>').join('');
    const sourceLink = item.recipe_url ? '<a href="' + escapeHtml(item.recipe_url) + '" target="_blank" rel="noreferrer">Open recipe source</a>' : '<span>No source URL saved</span>';
    const recipeId = item.kitchenowl_recipe_id ? '<span>KitchenOwl #' + escapeHtml(item.kitchenowl_recipe_id) + '</span>' : '';
    const resolveButton = resolved ? '' : '<button type="button" class="ghost" data-import-flag-resolve="' + escapeHtml(item.id) + '">Mark resolved</button>';
    return `
      <article class="failure-card" data-import-flag-id="${escapeHtml(item.id)}">
        <div class="suggestion-top">
          <div>
            <p class="section-label">${escapeHtml(item.issue_type || 'issue')}</p>
            <h3>${escapeHtml(item.recipe_title || item.recipe_url || 'KitchenOwl recipe issue')}</h3>
          </div>
          <p class="pill ${resolved ? 'status-resolved' : 'status-open'}">${escapeHtml(status)}</p>
        </div>
        <p>${escapeHtml(item.notes || '')}</p>
        <div class="chip-list">
          ${recipeId}
          <span>Flags: ${escapeHtml(item.flag_count || 1)}</span>
          <span>Last flagged: ${escapeHtml(item.last_flagged_at || item.updated_at || item.created_at || '')}</span>
        </div>
        <div class="flag-meta">${sourceLink}</div>
        <ul class="flag-history">${history || '<li>No extra notes logged yet.</li>'}</ul>
        <div class="action-row">${resolveButton}</div>
      </article>
    `;
  }).join('');
}


async function loadDashboard() {
  const data = await fetchJson('/api/dashboard/summary');
  const counts = data.counts || {};
  const profile = data.profile || {};
  const setText = (id, value) => {
    const el = document.getElementById(id);
    if (el) el.textContent = value;
  };
  setText('count-suggested', counts.suggested || 0);
  setText('count-approved', counts.approved || 0);
  setText('count-saved', counts.saved || 0);
  setText('count-rejected', counts.rejected || 0);
  setText('count-plans', counts.plans || 0);
  setText('count-failed-searches', counts.failed_searches || 0);
  setText('count-import-flags', counts.open_import_flags || 0);
  setText('backend-note', 'Storage backend: ' + data.backend);

  const blacklist = document.getElementById('blacklist-items');
  if (blacklist) {
    const items = profile.soft_blacklist || [];
    if (!items.length) {
      blacklist.textContent = 'No soft blacklist items yet.';
    } else {
      blacklist.classList.remove('muted');
      blacklist.innerHTML = items.map((item) => '<button class="chip-toggle ' + (item.enabled ? 'active' : '') + '" type="button" data-blacklist-name="' + escapeHtml(item.name) + '" data-blacklist-enabled="' + (item.enabled ? '1' : '0') + '">' + escapeHtml(item.name) + '</button>').join('');
    }
  }

  const notes = document.getElementById('profile-notes');
  if (notes) notes.value = profile.notes || '';
  const threshold = document.getElementById('auto-import-threshold');
  if (threshold) threshold.value = profile.auto_import_threshold || 96;
  const enabled = document.getElementById('auto-import-enabled');
  if (enabled) enabled.checked = Boolean(profile.auto_import_enabled);

  renderSuggestionCards(document.getElementById('suggestion-list'), data.recent_suggestions || []);
  renderFailureCards(document.getElementById('failed-search-preview'), data.recent_failed_searches || []);
  renderImportFlagCards(document.getElementById('import-flag-preview'), data.recent_import_flags || []);
}

async function handleDecision(event) {
  const button = event.target.closest('button[data-decision]');
  if (!button) return;
  const card = button.closest('[data-suggestion-id]');
  const suggestionId = card && card.dataset ? card.dataset.suggestionId : '';
  const decision = button.dataset.decision;
  if (!suggestionId || !decision) return;
  const failurePanel = card ? card.closest('[data-failure-results]') : null;
  const buttons = card ? Array.from(card.querySelectorAll('button')) : [button];
  const originalText = button.textContent;
  const workingText = decision === 'approve'
    ? 'Adding to KitchenOwl…'
    : (decision === 'later' ? 'Saving…' : 'Rejecting…');
  buttons.forEach((entry) => { entry.disabled = true; });
  button.textContent = workingText;
  setStatusMessage(workingText, 'muted');
  try {
    const data = await fetchJson('/api/suggestions/' + encodeURIComponent(suggestionId) + '/decision', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({decision, import_to_kitchenowl: decision === 'approve'}),
    });

    const suggestion = data.suggestion || {};
    const title = suggestion.title || 'Recipe';
    const kitchenowl = data.kitchenowl || {};
    if (decision === 'approve') {
      const outcome = kitchenowl.status || suggestion.kitchenowl_status || 'updated';
      const verb = outcome === 'duplicate' ? 'already exists in' : 'added to';
      setStatusMessage(title + ' ' + verb + ' KitchenOwl. Status: ' + outcome + '.', 'pill pill-good');
    } else if (decision === 'later') {
      setStatusMessage(title + ' saved for later review.', 'pill pill-good');
    } else {
      setStatusMessage(title + ' rejected.', 'pill pill-good');
    }

    await loadDashboard();
    if (failurePanel && card) {
      card.remove();
      const remaining = failurePanel.querySelectorAll('[data-suggestion-id]');
      if (!remaining.length) {
        failurePanel.innerHTML = '<p class="muted">No actionable recipes left in this review set.</p>';
        failurePanel.classList.remove('muted');
      }
      const failureCard = failurePanel.closest('[data-failure-id]');
      if (failureCard) {
        const count = failureCard.querySelector('.failure-count');
        if (count) {
          const currentText = count.textContent || '';
          const match = currentText.match(/(\d+)/);
          if (match) {
            const nextCount = Math.max(0, Number(match[1]) - 1);
            count.textContent = 'Current matching recipes: ' + String(nextCount);
          }
        }
      }
    }
    if (document.getElementById('search-results')) {
      await loadSuggestionsInto(document.getElementById('search-results'));
    }
    return data;
  } catch (error) {
    setStatusMessage('KitchenOwl action failed: ' + error.message, 'pill status-open');
    alert(error.message);
  } finally {
    buttons.forEach((entry) => { entry.disabled = false; });
    button.textContent = originalText;
  }
}

async function createImportFlag(payload) {
  return fetchJson('/api/import-flags', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify(payload),
  });
}


async function loadImportsInto(target) {
  const data = await fetchJson('/api/import-flags');
  renderImportFlagCards(target, data.items || []);
}


async function loadSuggestionsInto(target) {
  const data = await fetchJson('/api/suggestions');
  renderSuggestionCards(target, data.items || []);
}

async function loadFailuresInto(target) {
  const data = await fetchJson('/api/failed-searches');
  renderFailureCards(target, data.items || []);
}

async function loadFailureResults(failureId, target, button) {
  if (!target || !failureId) return;
  if (button) {
    button.disabled = true;
    button.textContent = 'Loading…';
  }
  target.textContent = 'Loading recipes for this search…';
  target.classList.add('muted');
  try {
    const data = await fetchJson('/api/failed-searches/' + encodeURIComponent(failureId) + '/results');
    const results = data.results || [];
    if (!results.length) {
      target.innerHTML = '<p class="muted">Still no usable recipes for this search.</p>';
      target.classList.remove('muted');
    } else {
      renderSuggestionCards(target, results);
    }
    const card = target.closest('[data-failure-id]');
    if (card && data.item) {
      const badge = card.querySelector('.pill');
      const count = card.querySelector('.failure-count');
      const list = card.querySelector('.failure-title-list');
      if (badge) {
        const resolved = String(data.item.status || '') === 'resolved-with-recipes';
        badge.textContent = String(data.item.status || 'open');
        badge.className = 'pill ' + (resolved ? 'status-resolved' : 'status-open');
      }
      if (count) {
        count.textContent = 'Current matching recipes: ' + String(data.item.result_count || 0);
      }
      if (list) {
        const titles = (data.item.sample_titles || []).map((entry) => '<li>' + escapeHtml(entry) + '</li>').join('');
        list.innerHTML = titles || '<li>No recovered recipe titles yet.</li>';
      }
    }
  } catch (error) {
    target.textContent = error.message;
  } finally {
    if (button) {
      button.disabled = false;
      button.textContent = 'View recipes';
    }
  }
}

function wireDashboard() {
  document.addEventListener('click', async (event) => {
    if (event.target.matches('button[data-decision]')) {
      await handleDecision(event);
      return;
    }
    if (event.target.matches('button[data-flag-suggestion]')) {
      const button = event.target;
      const suggestionId = button.dataset.flagSuggestion;
      const issueType = window.prompt('What kind of import issue is this? (formatting, ingredients, timings, duplicate, other)', 'formatting');
      if (!issueType) return;
      const notes = window.prompt('What went wrong with this import?');
      if (!notes) return;
      await createImportFlag({suggestion_id: suggestionId, issue_type: issueType, notes});
      await loadDashboard();
      const importsTarget = document.getElementById('import-flags-list');
      if (importsTarget) await loadImportsInto(importsTarget);
      return;
    }
    if (event.target.matches('button[data-import-flag-resolve]')) {
      const button = event.target;
      const importFlagId = button.dataset.importFlagResolve;
      const resolutionNotes = window.prompt('Optional resolution note:', '');
      await fetchJson('/api/import-flags/' + encodeURIComponent(importFlagId) + '/resolve', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({resolution_notes: resolutionNotes || ''}),
      });
      await loadDashboard();
      const importsTarget = document.getElementById('import-flags-list');
      if (importsTarget) await loadImportsInto(importsTarget);
      return;
    }
    if (event.target.matches('button[data-failure-open]')) {
      const button = event.target;
      const failureId = button.dataset.failureOpen;
      const card = button.closest('[data-failure-id]');
      const target = card ? card.querySelector('[data-failure-results]') : null;
      await loadFailureResults(failureId, target, button);
      return;
    }
    if (event.target.matches('button[data-blacklist-name]')) {
      const name = event.target.dataset.blacklistName;
      const enabled = event.target.dataset.blacklistEnabled !== '1';
      await fetchJson('/api/profile/blacklist', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({name, enabled}),
      });
      await loadDashboard();
    }
  });

  const seedButton = document.getElementById('seed-button');
  if (seedButton) {
    seedButton.addEventListener('click', async () => {
      seedButton.disabled = true;
      seedButton.textContent = 'Seeding…';
      try {
        await fetchJson('/api/search/seed', {method: 'POST'});
        await loadDashboard();
      } catch (error) {
        alert(error.message);
      } finally {
        seedButton.disabled = false;
        seedButton.textContent = 'Seed suggestion queue';
      }
    });
  }

  const blacklistForm = document.getElementById('blacklist-form');
  if (blacklistForm) {
    blacklistForm.addEventListener('submit', async (event) => {
      event.preventDefault();
      const input = document.getElementById('blacklist-name');
      if (!input.value.trim()) return;
      await fetchJson('/api/profile/blacklist', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({name: input.value.trim(), enabled: true}),
      });
      input.value = '';
      await loadDashboard();
    });
  }

  const settingsForm = document.getElementById('settings-form');
  if (settingsForm) {
    settingsForm.addEventListener('submit', async (event) => {
      event.preventDefault();
      await fetchJson('/api/profile/settings', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
          notes: document.getElementById('profile-notes').value,
          auto_import_threshold: Number(document.getElementById('auto-import-threshold').value || 96),
          auto_import_enabled: document.getElementById('auto-import-enabled').checked,
        }),
      });
      await loadDashboard();
    });
  }
}

function wireSearch() {
  const form = document.getElementById('search-form');
  const urlForm = document.getElementById('url-form');
  const queryInput = document.getElementById('search-query');
  const submitSearch = async (query) => {
    const target = document.getElementById('search-results');
    const status = document.getElementById('search-status');
    if (status) status.textContent = '';
    target.textContent = 'Searching and normalizing recipes…';
    target.classList.add('muted');
    const data = await fetchJson('/api/search', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({query, limit: 10}),
    });
    if (status && data.needs_attention) {
      status.textContent = 'No recipes came back for that search. Doris logged it as a search failure for troubleshooting.';
    } else if (status) {
      status.textContent = '';
    }
    renderSuggestionCards(target, data.results || []);
  };
  const submitUrl = async (url) => {
    const target = document.getElementById('search-results');
    const status = document.getElementById('search-status');
    if (status) status.textContent = '';
    target.textContent = 'Fetching and formatting that recipe link…';
    target.classList.add('muted');
    const data = await fetchJson('/api/search/url', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({url}),
    });
    if (status) {
      status.textContent = 'Recipe link ingested and queued for review.';
    }
    renderSuggestionCards(target, data.suggestion ? [data.suggestion] : []);
  };
  document.querySelectorAll('.example-query').forEach((button) => {
    button.addEventListener('click', async () => {
      const query = button.dataset.query || '';
      if (queryInput) {
        queryInput.value = query;
      }
      try {
        await submitSearch(query);
      } catch (error) {
        const target = document.getElementById('search-results');
        const status = document.getElementById('search-status');
        if (status) status.textContent = 'Search failed.';
        target.textContent = error.message;
      }
    });
  });
  if (form) {
    form.addEventListener('submit', async (event) => {
      event.preventDefault();
      const query = queryInput ? queryInput.value.trim() : '';
      if (!query) return;
      try {
        await submitSearch(query);
      } catch (error) {
        const target = document.getElementById('search-results');
        const status = document.getElementById('search-status');
        if (status) status.textContent = 'Search failed.';
        target.textContent = error.message;
      }
    });
  }
  if (!urlForm) return;
  urlForm.addEventListener('submit', async (event) => {
    event.preventDefault();
    const url = document.getElementById('recipe-url').value.trim();
    if (!url) return;
    try {
      await submitUrl(url);
    } catch (error) {
      const target = document.getElementById('search-results');
      const status = document.getElementById('search-status');
      if (status) status.textContent = 'Recipe link import failed.';
      target.textContent = error.message;
    }
  });
}

function wirePlanner() {
  const form = document.getElementById('planner-form');
  if (!form) return;
  const weekStart = document.getElementById('week-start');
  if (weekStart && !weekStart.value) {
    weekStart.value = new Date().toISOString().slice(0, 10);
  }
  form.addEventListener('submit', async (event) => {
    event.preventDefault();
    const anchors = document.getElementById('planner-anchors').value;
    const target = document.getElementById('plan-output');
    target.textContent = 'Building the week…';
    target.classList.add('muted');
    try {
      const data = await fetchJson('/api/planner/generate', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
          week_start: document.getElementById('week-start').value,
          target_meals: Number(document.getElementById('target-meals').value || 5),
          anchors,
        }),
      });
      const plan = data.plan;
      const shared = (plan.shared_ingredients || []).map((item) => '<span>' + escapeHtml(item) + '</span>').join('');
      target.classList.remove('muted');
      target.innerHTML = `
        <article class="plan-summary">
          <p class="section-label">Week of ${escapeHtml(plan.week_start)}</p>
          <h3>${escapeHtml(plan.summary)}</h3>
          <div class="chip-list">${shared || '<span>No shared ingredients landed yet.</span>'}</div>
        </article>
        ${(plan.meals || []).map((meal, index) => `
          <article class="plan-card">
            <p class="score-pill">Meal ${index + 1}</p>
            <h3><a href="${escapeHtml(meal.url)}" target="_blank" rel="noreferrer">${escapeHtml(meal.title)}</a></h3>
            <p class="muted">${escapeHtml(meal.note)}</p>
            <div class="chip-list">${(meal.ingredients || []).slice(0, 8).map((item) => '<span>' + escapeHtml(item) + '</span>').join('')}</div>
          </article>
        `).join('')}
      `;
    } catch (error) {
      target.textContent = error.message;
    }
  });
}

function wireImports() {
  const target = document.getElementById('import-flags-list');
  if (target) {
    loadImportsInto(target).catch(() => {
      target.textContent = 'Could not load import flags.';
    });
  }

  const form = document.getElementById('import-flag-form');
  if (!form) return;
  form.addEventListener('submit', async (event) => {
    event.preventDefault();
    const urlEl = document.getElementById('import-flag-url');
    const titleEl = document.getElementById('import-flag-title');
    const typeEl = document.getElementById('import-flag-type');
    const notesEl = document.getElementById('import-flag-notes');
    const payload = {
      recipe_url: urlEl ? urlEl.value.trim() : '',
      recipe_title: titleEl ? titleEl.value.trim() : '',
      issue_type: typeEl ? typeEl.value : 'formatting',
      notes: notesEl ? notesEl.value.trim() : '',
    };
    if (!payload.notes) {
      alert('Tell me what is wrong with the import first.');
      return;
    }
    await createImportFlag(payload);
    if (urlEl) urlEl.value = '';
    if (titleEl) titleEl.value = '';
    if (notesEl) notesEl.value = '';
    await loadDashboard();
    if (target) await loadImportsInto(target);
  });
}


function wireFailures() {
  const target = document.getElementById('failures-list');
  if (target) {
    loadFailuresInto(target).catch(() => {
      target.textContent = 'Could not load failed searches.';
    });
  }
  const refresh = document.getElementById('refresh-failures');
  if (refresh && target) {
    refresh.addEventListener('click', async () => {
      refresh.disabled = true;
      refresh.textContent = 'Refreshing…';
      try {
        const data = await fetchJson('/api/failed-searches/refresh', {method: 'POST'});
        renderFailureCards(target, data.items || []);
      } finally {
        refresh.disabled = false;
        refresh.textContent = 'Refresh progress';
      }
    });
  }
}

loadDashboard().catch(() => {});
wireDashboard();
wireSearch();
wirePlanner();
wireImports();
wireFailures();
if (document.getElementById('search-results')) {
  loadSuggestionsInto(document.getElementById('search-results')).catch(() => {});
}
