let watchlistSearchTimer = null;
let assistantRenderFrame = 0;


window.XiaobaiPageModules.register("review", ["reviewWorkspaceView"], {
  bind: bindReviewEvents,
  enter: ["loadReview"],
});

async function loadReviewWorkspace() {
  try {
    const [watchlistPayload, notesPayload, tradesPayload] = await Promise.all([
      apiRequest(`/api/watchlist?trade_date=${encodeURIComponent(elements.tradeDate.value)}`),
      apiRequest("/api/notes?scope=daily"),
      apiRequest("/api/trades"),
    ]);
    state.watchlist = watchlistPayload.items || [];
    state.notes = notesPayload.items || [];
    state.tradeEntries = tradesPayload.items || [];
    state.tradeSummary = tradesPayload.summary || {};
    setText("reviewDataDate", displayCompactDate(elements.tradeDate.value));
    renderWatchlist();
    renderNotesHistory(state.notes, document.querySelector("#notesHistory"), false);
    setText("notesCount", `${state.notes.length} 条`);
    renderTradeLog();
    populateJournalForm();
  } catch (error) {
    showToast(error.message || "我的复盘加载失败");
  }
}

function renderWatchlist() {
  setText("watchlistCount", `${state.watchlist.length} 只`);
  const body = document.querySelector("#watchlistTableBody");
  body.innerHTML = state.watchlist.map((item) => `
    <tr data-code="${escapeHtml(item.code)}"><td><span class="review-watch-mark ${escapeHtml(item.color)}" title="${escapeHtml(item.color)}">★</span></td>
      <td><span class="stock-cell"><strong class="stock-name sname">${escapeHtml(item.name)}</strong><small class="stock-code scode">${escapeHtml(item.code)}</small></span></td>
      <td>${escapeHtml(item.sector || "其他")}</td>
      <td class="number num ${item.change == null ? "" : changeClass(item.change)}">${formatWatchMetric(item.change)}</td>
      <td class="number num ${item.return_5d == null ? "" : changeClass(item.return_5d)}">${formatWatchMetric(item.return_5d)}</td>
      <td class="number num"><strong class="watch-attention-score">${item.attention_score == null ? "" : formatNumber(item.attention_score, 1)}</strong></td>
      <td><span class="watch-remark" title="${escapeHtml(item.remark || "尚未填写跟踪备注")}">${escapeHtml(item.remark || "尚未填写")}</span></td>
      <td><span class="review-row-actions"><button class="table-action" type="button" data-watch-remark="${escapeHtml(item.code)}">备注</button>
      <button class="table-action down" type="button" data-watch-delete="${escapeHtml(item.code)}" aria-label="移除 ${escapeHtml(item.name)}">移除</button></span></td></tr>
  `).join("");
  document.querySelector("#watchlistEmpty").hidden = state.watchlist.length > 0;
  body.querySelectorAll("[data-watch-remark]").forEach((button) => {
    button.addEventListener("click", () => {
      const item = state.watchlist.find((row) => row.code === button.dataset.watchRemark);
      openWatchlistDialog(item);
    });
  });
  body.querySelectorAll("[data-watch-delete]").forEach((button) => {
    button.addEventListener("click", () => removeWatchlist(button.dataset.watchDelete));
  });
  bindStockRows(body);
}

function formatWatchMetric(value) {
  if (value == null || !Number.isFinite(Number(value))) return "";
  return signed(value);
}

function openWatchlistDialog(item = null) {
  clearTimeout(watchlistSearchTimer);
  state.watchlistSelection = item ? {
    code: item.code,
    name: item.name,
    sector: item.sector || "其他",
    color: item.color || "red",
  } : null;
  state.watchlistSearchResults = [];
  setText("watchlistDialogTitle", item ? "编辑跟踪备注" : "添加自选");
  document.querySelector("#watchlistRemark").value = item?.remark || "";
  document.querySelector("#watchlistSearchInput").value = "";
  document.querySelector("#watchlistSearchResults").innerHTML = "";
  syncWatchlistSelection(Boolean(item));
  openModalDialog(elements.watchlistDialog);
  requestAnimationFrame(() => (item ? document.querySelector("#watchlistRemark") : document.querySelector("#watchlistSearchInput")).focus());
}

function closeWatchlistDialog() {
  clearTimeout(watchlistSearchTimer);
  if (elements.watchlistDialog.open) elements.watchlistDialog.close();
}

function clearWatchlistSelection() {
  state.watchlistSelection = null;
  syncWatchlistSelection(false);
  document.querySelector("#watchlistSearchInput").focus();
}

function syncWatchlistSelection(editing = false) {
  const item = state.watchlistSelection;
  document.querySelector("#watchlistSearchField").hidden = Boolean(item);
  document.querySelector("#watchlistSelection").hidden = !item;
  document.querySelector("#changeWatchlistSelection").hidden = editing;
  document.querySelector("#saveWatchlist").disabled = !item;
  if (!item) return;
  setText("watchlistSelectionName", item.name || "--");
  setText("watchlistSelectionCode", item.code || "--");
  setText("watchlistSelectionSector", item.sector || "其他");
  refreshIcons();
}

function scheduleWatchlistSearch() {
  clearTimeout(watchlistSearchTimer);
  const query = document.querySelector("#watchlistSearchInput").value.trim();
  if (!query) {
    document.querySelector("#watchlistSearchResults").innerHTML = "";
    return;
  }
  document.querySelector("#watchlistSearchResults").innerHTML = '<div class="watchlist-search-status">正在查找股票</div>';
  watchlistSearchTimer = setTimeout(() => runWatchlistSearch(query), 160);
}

async function runWatchlistSearch(query) {
  const sequence = ++state.watchlistSearchRequestSequence;
  try {
    const params = new URLSearchParams({ q: query, trade_date: elements.tradeDate.value });
    const payload = await apiRequest(`/api/search?${params}`);
    if (sequence !== state.watchlistSearchRequestSequence) return;
    state.watchlistSearchResults = payload.groups?.stocks || [];
    document.querySelector("#watchlistSearchResults").innerHTML = state.watchlistSearchResults.map((item, index) => `
      <button type="button" data-watchlist-result="${index}"><span><strong>${escapeHtml(item.name)}</strong><small>${escapeHtml(item.industry || "其他")}</small></span><b>${escapeHtml(item.code)}</b></button>
    `).join("") || '<div class="watchlist-search-status">没有找到匹配的股票</div>';
  } catch (error) {
    document.querySelector("#watchlistSearchResults").innerHTML = `<div class="watchlist-search-status">${escapeHtml(error.message || "搜索失败")}</div>`;
  }
}

function handleWatchlistSearchResult(event) {
  const button = event.target.closest("[data-watchlist-result]");
  if (!button) return;
  const item = state.watchlistSearchResults[number(button.dataset.watchlistResult)];
  if (!item) return;
  state.watchlistSelection = {
    code: item.code,
    name: item.name,
    sector: item.industry || "其他",
    color: "red",
  };
  syncWatchlistSelection(false);
}

async function saveWatchlistFromDialog(event) {
  event.preventDefault();
  const item = state.watchlistSelection;
  if (!item) return;
  const button = document.querySelector("#saveWatchlist");
  button.disabled = true;
  try {
    await apiRequest("/api/watchlist", "POST", {
      code: item.code,
      name: item.name,
      sector: item.sector || "其他",
      color: item.color || "red",
      remark: document.querySelector("#watchlistRemark").value.trim(),
    });
    closeWatchlistDialog();
    await loadReviewWorkspace();
    showToast(state.watchlist.some((row) => row.code === item.code) ? "自选跟踪已保存" : "已加入自选");
  } catch (error) {
    showToast(error.message || "自选保存失败");
    button.disabled = false;
  }
}

async function toggleActiveWatchlist() {
  const stock = state.activeStock;
  if (!stock?.code) return;
  const isWatched = Boolean(state.stockDetail?.stock?.watchlist || state.watchlist.some((item) => item.code === stock.code));
  try {
    if (isWatched) {
      await apiRequest(`/api/watchlist/${stock.code}`, "DELETE");
      state.watchlist = state.watchlist.filter((item) => item.code !== stock.code);
      if (state.stockDetail?.stock) state.stockDetail.stock.watchlist = null;
      showToast("已移出自选");
    } else {
      const payload = await apiRequest("/api/watchlist", "POST", {
        code: stock.code,
        name: stock.name || "--",
        sector: stock.sector || "其他",
        color: "red",
      });
      state.watchlist = payload.items || state.watchlist;
      if (state.stockDetail?.stock) state.stockDetail.stock.watchlist = state.watchlist.find((item) => item.code === stock.code);
      showToast("已加入自选");
    }
    updateWatchButton();
    renderWatchlist();
  } catch (error) {
    showToast(error.message);
  }
}

function updateWatchButton() {
  const code = state.activeStock?.code;
  const watched = Boolean(state.stockDetail?.stock?.watchlist || state.watchlist.some((item) => item.code === code));
  setText("watchStockButton", watched ? "移出自选" : "加入自选");
}

async function removeWatchlist(code) {
  try {
    await apiRequest(`/api/watchlist/${code}`, "DELETE");
    state.watchlist = state.watchlist.filter((item) => item.code !== code);
    renderWatchlist();
    showToast("已移出自选");
  } catch (error) {
    showToast(error.message);
  }
}

async function saveJournal(event) {
  event.preventDefault();
  try {
    await apiRequest("/api/notes", "POST", {
      trade_date: document.querySelector("#journalDate").value,
      id: state.editingDailyNoteId || undefined,
      summary: document.querySelector("#journalSummary").value,
      content: document.querySelector("#journalContent").value,
      plan: document.querySelector("#journalPlan").value,
    });
    await loadReviewWorkspace();
    showToast("每日复盘已保存");
  } catch (error) {
    showToast(error.message);
  }
}

function populateJournalForm() {
  const selectedDate = document.querySelector("#journalDate").value.replaceAll("-", "");
  const note = state.notes.find((item) => String(item.trade_date).replaceAll("-", "") === selectedDate);
  state.editingDailyNoteId = number(note?.id);
  document.querySelector("#journalSummary").value = note?.summary || "";
  document.querySelector("#journalContent").value = note?.content || "";
  document.querySelector("#journalPlan").value = note?.plan || "";
}

function openTradeLogDialog() {
  resetTradeLogForm();
  openModalDialog(elements.tradeLogDialog);
  requestAnimationFrame(() => document.querySelector("#tradeLogCode").focus());
}

function closeTradeLogDialog() {
  if (elements.tradeLogDialog.open) elements.tradeLogDialog.close();
  else resetTradeLogForm();
}

async function saveTradeLog(event) {
  event.preventDefault();
  const button = document.querySelector("#saveTradeLog");
  button.disabled = true;
  try {
    const payload = await apiRequest("/api/trades", "POST", {
      id: state.editingTradeId || undefined,
      trade_date: document.querySelector("#tradeLogDate").value,
      code: document.querySelector("#tradeLogCode").value.trim(),
      name: document.querySelector("#tradeLogName").value.trim(),
      action: document.querySelector("#tradeLogAction").value,
      price: document.querySelector("#tradeLogPrice").value,
      quantity: document.querySelector("#tradeLogQuantity").value,
      position_pct: document.querySelector("#tradeLogPosition").value,
      pnl_amount: document.querySelector("#tradeLogPnlAmount").value,
      pnl_pct: document.querySelector("#tradeLogPnlPct").value,
      emotion: document.querySelector("#tradeLogEmotion").value,
      tags: document.querySelector("#tradeLogTags").value,
      thesis: document.querySelector("#tradeLogThesis").value,
      execution: document.querySelector("#tradeLogExecution").value,
    });
    state.tradeEntries = payload.items || [];
    state.tradeSummary = payload.summary || {};
    renderTradeLog();
    closeTradeLogDialog();
    showToast("交易记录已保存");
  } catch (error) {
    showToast(error.message || "交易记录保存失败");
  } finally {
    button.disabled = false;
  }
}

function resetTradeLogForm() {
  state.editingTradeId = 0;
  document.querySelector("#tradeLogForm").reset();
  document.querySelector("#tradeLogDate").value = elements.tradeDate.value || todayString();
  document.querySelector("#tradeLogQuantity").value = "0";
  document.querySelector("#tradeLogPosition").value = "0";
  setText("tradeLogDialogTitle", "交易日志");
  setText("saveTradeLog", "保存交易");
}

function editTradeLog(id) {
  const item = state.tradeEntries.find((entry) => number(entry.id) === id);
  if (!item) return;
  state.editingTradeId = id;
  document.querySelector("#tradeLogDate").value = displayCompactDate(item.trade_date);
  document.querySelector("#tradeLogCode").value = item.code;
  document.querySelector("#tradeLogName").value = item.name;
  document.querySelector("#tradeLogAction").value = item.action;
  document.querySelector("#tradeLogPrice").value = item.price;
  document.querySelector("#tradeLogQuantity").value = item.quantity;
  document.querySelector("#tradeLogPosition").value = item.position_pct;
  document.querySelector("#tradeLogPnlAmount").value = item.pnl_amount ?? "";
  document.querySelector("#tradeLogPnlPct").value = item.pnl_pct ?? "";
  document.querySelector("#tradeLogEmotion").value = item.emotion;
  document.querySelector("#tradeLogTags").value = (item.tags || []).join(", ");
  document.querySelector("#tradeLogThesis").value = item.thesis || "";
  document.querySelector("#tradeLogExecution").value = item.execution || "";
  setText("tradeLogDialogTitle", "编辑交易日志");
  setText("saveTradeLog", "保存修改");
  openModalDialog(elements.tradeLogDialog);
  requestAnimationFrame(() => document.querySelector("#tradeLogCode").focus());
}

async function handleTradeLogAction(event) {
  const button = event.target.closest("[data-trade-action]");
  if (!button) return;
  const id = number(button.dataset.tradeId);
  if (button.dataset.tradeAction === "edit") {
    editTradeLog(id);
    return;
  }
  if (!window.confirm("确定删除这条交易记录吗？")) return;
  try {
    const payload = await apiRequest(`/api/trades/${id}`, "DELETE");
    state.tradeEntries = payload.items || [];
    state.tradeSummary = payload.summary || {};
    if (state.editingTradeId === id) resetTradeLogForm();
    renderTradeLog();
    showToast("交易记录已删除");
  } catch (error) {
    showToast(error.message || "交易记录删除失败");
  }
}

function renderTradeLog() {
  const summary = state.tradeSummary || {};
  setText("tradeLogCount", `${state.tradeEntries.length} 条`);
  document.querySelector("#tradeLogSummary").innerHTML = [
    ["记录", `${number(summary.total)} 条`],
    ["已实现", `${number(summary.realized)} 条`],
    ["胜率", summary.win_rate == null ? "--" : `${formatNumber(summary.win_rate, 1)}%`],
    ["累计盈亏", summary.pnl_amount == null ? "--" : `${number(summary.pnl_amount) > 0 ? "+" : ""}${formatNumber(summary.pnl_amount, 2)}`],
    ["平均仓位", summary.average_position == null ? "--" : `${formatNumber(summary.average_position, 1)}%`],
  ].map(([label, value]) => `<div><span>${label}</span><strong>${value}</strong></div>`).join("");
  document.querySelector("#tradeLogEmpty").hidden = state.tradeEntries.length > 0;
  document.querySelector("#tradeLogTableBody").innerHTML = state.tradeEntries.map((item) => `
    <tr data-code="${escapeHtml(item.code)}">
      <td>${displayCompactDate(item.trade_date)}</td>
      <td><span class="stock-cell"><strong class="sname">${escapeHtml(item.name)}</strong><small class="stock-code scode">${escapeHtml(item.code)}</small></span></td>
      <td><span class="trade-action trade-action-${escapeHtml(item.action)}">${escapeHtml(item.action_label)}</span></td>
      <td class="number num">${item.position_pct == null ? "" : formatNumber(item.position_pct, 1)}</td>
      <td class="number num ${item.pnl_pct == null ? "" : changeClass(item.pnl_pct)}">${item.pnl_pct == null ? "" : signed(item.pnl_pct)}</td>
      <td class="number num ${item.pnl_amount == null ? "" : changeClass(item.pnl_amount)}">${item.pnl_amount == null ? "" : signed(item.pnl_amount)}</td>
      <td><span class="trade-emotion">${escapeHtml(item.emotion_label)}</span><div class="trade-tags">${(item.tags || []).map((tag) => `<em>${escapeHtml(tag)}</em>`).join("")}</div></td>
      <td class="trade-copy" title="交易逻辑：${escapeHtml(item.thesis || "")}；执行复核：${escapeHtml(item.execution || "")}"><strong>${escapeHtml(item.thesis || "")}</strong><small>${escapeHtml(item.execution || "尚未填写执行复核")}</small></td>
      <td><div class="trade-row-actions"><button class="table-action" type="button" data-trade-action="edit" data-trade-id="${number(item.id)}">编辑</button><button class="table-action down" type="button" data-trade-action="delete" data-trade-id="${number(item.id)}">删除</button></div></td>
    </tr>
  `).join("");
  bindStockRows(document.querySelector("#tradeLogTableBody"));
}

async function saveStockNote(event) {
  event.preventDefault();
  if (!state.activeStock?.code) return;
  try {
    await apiRequest("/api/notes", "POST", {
      code: state.activeStock.code,
      stock_name: state.activeStock.name || "--",
      trade_date: elements.tradeDate.value,
      content: document.querySelector("#stockNoteContent").value,
      plan: document.querySelector("#stockNotePlan").value,
    });
    document.querySelector("#stockNoteContent").value = "";
    document.querySelector("#stockNotePlan").value = "";
    const payload = await apiRequest(`/api/notes?scope=stock&code=${encodeURIComponent(state.activeStock.code)}`);
    state.stockDetail.notes = payload.items || [];
    renderStockNotes(state.stockDetail.notes);
    showToast("个股笔记已保存");
  } catch (error) {
    showToast(error.message);
  }
}

async function saveReasonOverride(event) {
  event.preventDefault();
  if (!state.activeStock?.code) return;
  const reason = document.querySelector("#reasonInput").value.trim();
  try {
    await apiRequest("/api/reasons", "POST", {
      trade_date: elements.tradeDate.value,
      code: state.activeStock.code,
      reason,
    });
    state.activeStock.reason = reason;
    for (const key of ["limits", "broken", "down_limits"]) {
      const row = state.dashboard?.[key]?.find((item) => item.code === state.activeStock.code);
      if (row) row.reason = reason;
    }
    setText("detailReason", reason);
    renderDashboard();
    showToast("事件逻辑已修订");
  } catch (error) {
    showToast(error.message);
  }
}

function renderMoneyflow(flow) {
  for (const [id, value] of [["flowNet", flow.net_million], ["flowLarge", flow.large_million], ["flowMedium", flow.medium_million], ["flowSmall", flow.small_million]]) {
    const element = document.getElementById(id);
    element.textContent = formatMoneyMillion(value);
    element.className = changeClass(value);
  }
}

function renderStockNotes(notes) {
  renderNotesHistory(notes, document.querySelector("#stockNotes"), true);
}

function renderNotesHistory(notes, container, compact) {
  container.innerHTML = notes.map((note) => `
    <article class="note-row">
      <div><time>${displayCompactDate(note.trade_date)}</time>${note.stock_name ? `<small>${escapeHtml(note.stock_name)}</small>` : ""}</div>
      ${!compact ? `<div class="note-block note-summary"><strong>盘面</strong><p>${escapeHtml(note.summary || "--")}</p></div>` : ""}
      <div class="note-block"><strong>复盘</strong><p>${escapeHtml(note.content || "--")}</p></div>
      <div class="note-block"><strong>计划</strong><p>${escapeHtml(note.plan || "--")}</p></div>
      <button class="table-action down" type="button" data-note-delete="${number(note.id)}">删除</button>
    </article>
  `).join("") || emptyStateHtml("暂无复盘记录");
  container.querySelectorAll("[data-note-delete]").forEach((button) => {
    button.addEventListener("click", () => deleteNote(number(button.dataset.noteDelete), compact));
  });
}

async function deleteNote(noteId, compact) {
  try {
    await apiRequest(`/api/notes/${noteId}`, "DELETE");
    if (compact && state.activeStock) {
      state.stockDetail.notes = state.stockDetail.notes.filter((note) => number(note.id) !== noteId);
      renderStockNotes(state.stockDetail.notes);
    } else {
      await loadReviewWorkspace();
    }
    showToast("笔记已删除");
  } catch (error) {
    showToast(error.message);
  }
}

async function loadAlerts(openDialog = false) {
  try {
    const query = new URLSearchParams({ status: state.alertFilter, as_of: todayString() });
    const payload = await apiRequest(`/api/alerts?${query}`);
    state.alerts = payload.items || [];
    state.alertUnreadCount = number(payload.unread_count);
    renderAlerts();
    if (openDialog) openModalDialog(elements.alertsDialog);
  } catch (error) {
    if (openDialog) showToast(error.message || "提醒加载失败");
  }
}

function openAlerts() {
  toggleHeaderCommandMenu(false);
  toggleAccountDropdown(false);
  document.querySelector("#alertDate").value ||= todayString();
  openModalDialog(elements.alertsDialog);
  loadAlerts();
}

function openStockReminder() {
  const stock = state.activeStock || {};
  document.querySelector("#alertTitle").value = `${stock.name || stock.code || "个股"}观察提醒`;
  document.querySelector("#alertCode").value = stock.code || "";
  document.querySelector("#alertDate").value = todayString();
  if (elements.stockDialog.open) elements.stockDialog.close();
  openAlerts();
  document.querySelector("#alertContent").focus();
}

function selectAlertFilter(filter) {
  state.alertFilter = filter === "unread" ? "unread" : "all";
  document.querySelectorAll("[data-alert-filter]").forEach((button) => {
    button.classList.toggle("active", button.dataset.alertFilter === state.alertFilter);
  });
  loadAlerts();
}

async function saveAlert(event) {
  event.preventDefault();
  const button = event.currentTarget.querySelector("button[type='submit']");
  button.disabled = true;
  try {
    const payload = await apiRequest("/api/alerts", "POST", {
      title: document.querySelector("#alertTitle").value.trim(),
      remind_date: document.querySelector("#alertDate").value,
      code: document.querySelector("#alertCode").value.trim(),
      content: document.querySelector("#alertContent").value.trim(),
    });
    event.currentTarget.reset();
    document.querySelector("#alertDate").value = todayString();
    state.alertFilter = "all";
    state.alerts = payload.items || [];
    state.alertUnreadCount = number(payload.unread_count);
    renderAlerts();
    showToast("提醒已保存");
  } catch (error) {
    showToast(error.message || "提醒保存失败");
  } finally {
    button.disabled = false;
  }
}

async function markAllAlertsRead() {
  try {
    await apiRequest("/api/alerts/read-all", "POST", { as_of: todayString() });
    await loadAlerts();
  } catch (error) {
    showToast(error.message || "提醒状态更新失败");
  }
}

async function handleAlertAction(event) {
  const button = event.target.closest("[data-alert-action]");
  if (!button) return;
  const id = number(button.dataset.alertId);
  if (!id) return;
  try {
    if (button.dataset.alertAction === "delete") {
      await apiRequest(`/api/alerts/${id}`, "DELETE");
    } else {
      await apiRequest(`/api/alerts/${id}/read`, "POST", {});
    }
    await loadAlerts();
  } catch (error) {
    showToast(error.message || "提醒操作失败");
  }
}

function renderAlerts() {
  const badge = document.querySelector("#alertBadge");
  badge.hidden = state.alertUnreadCount <= 0;
  badge.textContent = state.alertUnreadCount > 99 ? "99+" : String(state.alertUnreadCount);
  document.querySelector("#alertButton").classList.toggle("has-alerts", state.alertUnreadCount > 0);
  setText("alertListCount", `${state.alerts.length} 条`);
  document.querySelectorAll("[data-alert-filter]").forEach((button) => {
    button.classList.toggle("active", button.dataset.alertFilter === state.alertFilter);
  });
  document.querySelector("#markAllAlertsRead").disabled = state.alertUnreadCount <= 0;
  const container = document.querySelector("#alertList");
  container.innerHTML = state.alerts.map((item) => {
    const upcoming = !item.due;
    const kindLabel = item.kind === "manual" ? "自定提醒" : item.kind === "strategy_t5" ? "跟踪完成" : "策略反馈";
    return `<article class="alert-item ${item.is_read ? "is-read" : "is-unread"} ${upcoming ? "is-upcoming" : ""}">
      <div class="alert-item-icon"><i data-lucide="${upcoming ? "calendar-clock" : item.kind === "manual" ? "bell" : "chart-no-axes-combined"}"></i></div>
      <div class="alert-item-copy">
        <div><span>${escapeHtml(kindLabel)}</span><time>${displayCompactDate(item.available_date)}</time></div>
        <strong>${escapeHtml(item.title)}</strong>
        ${item.content ? `<p>${escapeHtml(item.content)}</p>` : ""}
        ${item.code ? `<button class="stock-preview-trigger alert-stock-link" type="button" data-code="${escapeHtml(item.code)}">${escapeHtml(item.code)}</button>` : ""}
      </div>
      <div class="alert-item-actions">
        ${!item.is_read && !upcoming ? `<button class="icon-button" type="button" data-alert-action="read" data-alert-id="${number(item.id)}" title="标为已读" aria-label="标为已读"><i data-lucide="check"></i></button>` : ""}
        <button class="icon-button" type="button" data-alert-action="delete" data-alert-id="${number(item.id)}" title="删除提醒" aria-label="删除提醒"><i data-lucide="trash-2"></i></button>
      </div>
    </article>`;
  }).join("") || emptyStateHtml("暂无提醒");
  bindStockRows(container);
  refreshIcons();
}

async function openReviewAssistant() {
  toggleHeaderCommandMenu(false);
  toggleAccountDropdown(false);
  openModalDialog(elements.assistantDialog);
  updateAssistantControls();
  if (!hasMemberAccess()) {
    document.querySelector("#closeAssistantDialog").focus();
    return;
  }
  try {
    const payload = await apiRequest("/api/assistant/messages");
    state.assistantMessages = payload.items || [];
    renderAssistantMessages();
  } catch (error) {
    showToast(error.message || "对话记录加载失败");
  }
  document.querySelector("#assistantQuestion").focus();
}

function useAssistantPrompt(prompt) {
  const input = document.querySelector("#assistantQuestion");
  input.value = prompt;
  input.focus();
}

async function sendAssistantQuestion(event) {
  event.preventDefault();
  if (state.assistantLoading) return;
  const input = document.querySelector("#assistantQuestion");
  const question = input.value.trim();
  if (!question) return;
  input.value = "";
  state.assistantMessages.push({ role: "user", content: question, context_date: elements.tradeDate.value.replaceAll("-", "") });
  state.assistantMessages.push({ role: "assistant", content: "", streaming: true, context_date: elements.tradeDate.value.replaceAll("-", "") });
  state.assistantLoading = true;
  state.assistantController = new AbortController();
  updateAssistantControls();
  renderAssistantMessages();
  try {
    await streamAssistantRequest(question, state.assistantController.signal, (chunk) => {
      const message = state.assistantMessages.at(-1);
      if (message?.role === "assistant") message.content += chunk;
      scheduleAssistantRender();
    });
    const message = state.assistantMessages.at(-1);
    if (message) message.streaming = false;
    setStatus("复盘助手回答完成");
  } catch (error) {
    const message = state.assistantMessages.at(-1);
    if (message?.role === "assistant") {
      message.streaming = false;
      message.error = true;
      if (!message.content) message.content = error.name === "AbortError" ? "已停止生成。" : error.message || "回答失败，请稍后重试。";
    }
    if (error.name !== "AbortError") showToast(error.message || "复盘助手回答失败");
  } finally {
    state.assistantLoading = false;
    state.assistantController = null;
    updateAssistantControls();
    renderAssistantMessages();
    input.focus();
  }
}

async function streamAssistantRequest(question, signal, onDelta) {
  await window.XiaobaiAPI.streamNdjson("/api/assistant/chat", {
    method: "POST",
    body: { question, trade_date: elements.tradeDate.value },
    signal,
    errorMessage: "复盘助手暂不可用",
    onEvent: (event) => {
      if (event.type === "delta") onDelta(String(event.content || ""));
    },
  });
}

function stopAssistantResponse() {
  state.assistantController?.abort();
}

async function clearAssistantConversation() {
  if (state.assistantLoading || !state.assistantMessages.length) return;
  if (!window.confirm("确定清空复盘助手的对话记录吗？")) return;
  try {
    await apiRequest("/api/assistant/messages", "DELETE");
    state.assistantMessages = [];
    renderAssistantMessages();
  } catch (error) {
    showToast(error.message || "对话记录清空失败");
  }
}

function scheduleAssistantRender() {
  if (assistantRenderFrame) return;
  assistantRenderFrame = requestAnimationFrame(() => {
    assistantRenderFrame = 0;
    renderAssistantMessages();
  });
}

function renderAssistantMessages() {
  const container = document.querySelector("#assistantMessages");
  container.innerHTML = state.assistantMessages.map((message) => `
    <article class="assistant-message ${message.role} ${message.error ? "is-error" : ""}">
      <div class="assistant-message-label">${message.role === "user" ? "我" : "复盘助手"}${message.context_date ? `<time>${displayCompactDate(message.context_date)}</time>` : ""}</div>
      <div class="assistant-message-content">${message.role === "assistant" ? (message.content ? formatMentorAnswer(message.content) : '<span class="assistant-thinking">正在整理复盘数据</span>') : escapeHtml(message.content)}</div>
      ${message.streaming ? '<span class="assistant-stream-caret" aria-hidden="true"></span>' : ""}
    </article>
  `).join("") || emptyStateHtml("可以从市场、策略或自己的交易记录开始复盘");
  updateAssistantControls();
  requestAnimationFrame(() => { container.scrollTop = container.scrollHeight; });
}

function updateAssistantControls() {
  const unlocked = hasMemberAccess();
  elements.assistantDialog.classList.toggle("member-locked", !unlocked);
  document.querySelector("#assistantMemberGate").hidden = unlocked;
  document.querySelector("#assistantMemberContent").setAttribute("aria-disabled", String(!unlocked));
  document.querySelector("#assistantQuestion").disabled = !unlocked || state.assistantLoading;
  document.querySelector("#sendAssistant").disabled = !unlocked || state.assistantLoading;
  document.querySelector("#stopAssistant").hidden = !unlocked || !state.assistantLoading;
  document.querySelector("#clearAssistantMessages").disabled = !unlocked || state.assistantLoading || !state.assistantMessages.length;
  document.querySelectorAll("[data-assistant-prompt]").forEach((button) => {
    button.disabled = !unlocked || state.assistantLoading;
  });
}


function bindReviewEvents() {
  document.querySelector("#alertButton").addEventListener("click", openAlerts);
  document.querySelector("#assistantButton").addEventListener("click", openReviewAssistant);
  document.querySelector("#closeAssistantDialog").addEventListener("click", () => elements.assistantDialog.close());
  document.querySelector("#assistantForm").addEventListener("submit", sendAssistantQuestion);
  document.querySelector("#stopAssistant").addEventListener("click", stopAssistantResponse);
  document.querySelector("#clearAssistantMessages").addEventListener("click", clearAssistantConversation);
  document.querySelectorAll("[data-assistant-prompt]").forEach((button) => {
    button.addEventListener("click", () => useAssistantPrompt(button.dataset.assistantPrompt));
  });
  document.querySelector("#closeAlertsDialog").addEventListener("click", () => elements.alertsDialog.close());
  document.querySelector("#alertForm").addEventListener("submit", saveAlert);
  document.querySelector("#markAllAlertsRead").addEventListener("click", markAllAlertsRead);
  document.querySelector("#alertList").addEventListener("click", handleAlertAction);
  document.querySelectorAll("[data-alert-filter]").forEach((button) => {
    button.addEventListener("click", () => selectAlertFilter(button.dataset.alertFilter));
  });
  document.querySelector("#journalForm").addEventListener("submit", saveJournal);
  document.querySelector("#journalDate").addEventListener("change", populateJournalForm);
  document.querySelector("#openWatchlistDialog").addEventListener("click", () => openWatchlistDialog());
  document.querySelector("#closeWatchlistDialog").addEventListener("click", closeWatchlistDialog);
  document.querySelector("#cancelWatchlistEdit").addEventListener("click", closeWatchlistDialog);
  document.querySelector("#changeWatchlistSelection").addEventListener("click", clearWatchlistSelection);
  document.querySelector("#watchlistSearchInput").addEventListener("input", scheduleWatchlistSearch);
  document.querySelector("#watchlistForm").addEventListener("submit", saveWatchlistFromDialog);
  document.querySelector("#watchlistSearchResults").addEventListener("click", handleWatchlistSearchResult);
  document.querySelector("#reviewHistoryToggle").addEventListener("click", (event) => {
    const panel = document.querySelector("#reviewHistoryPanel");
    const expanded = event.currentTarget.getAttribute("aria-expanded") === "true";
    event.currentTarget.setAttribute("aria-expanded", String(!expanded));
    event.currentTarget.querySelector("span").textContent = expanded ? "历史复盘" : "收起历史";
    panel.hidden = expanded;
    if (!expanded) panel.scrollIntoView({ behavior: "smooth", block: "nearest" });
  });
  document.querySelector("#openTradeLogDialog").addEventListener("click", openTradeLogDialog);
  document.querySelector("#closeTradeLogDialog").addEventListener("click", closeTradeLogDialog);
  document.querySelector("#tradeLogForm").addEventListener("submit", saveTradeLog);
  document.querySelector("#cancelTradeEdit").addEventListener("click", closeTradeLogDialog);
  elements.tradeLogDialog.addEventListener("close", resetTradeLogForm);
  document.querySelector("#tradeLogTableBody").addEventListener("click", handleTradeLogAction);
  document.querySelector("#stockNoteForm").addEventListener("submit", saveStockNote);
  document.querySelector("#watchStockButton").addEventListener("click", toggleActiveWatchlist);
  document.querySelector("#stockHeavenButton").addEventListener("click", openActiveStockInHeaven);
  document.querySelector("#stockReminderButton").addEventListener("click", openStockReminder);
}
