import type { ChatMessage } from '@genesislcap/foundation-ai';

/**
 * Display-only session-restore notices (GENC-1351 §6): the "Restored…" divider on
 * a successful reload restore, and the "Couldn't restore…" divider when a saved
 * session existed but couldn't be brought back (timeout / error / unusable
 * snapshot). Both are `system-event` rows, stripped from the persisted snapshot so
 * they never become permanent history — and, unlike agent-switch indicators, they
 * belong to the persistence ("remember chat") feature, so `filterVisibleMessages`
 * gates them by `showRestoreNotices` rather than `showAgentSwitchIndicator`.
 *
 * @internal
 */
export const SESSION_RESTORED_LABEL = 'Restored previous conversation';
/** @internal */
export const SESSION_RESTORE_FAILED_LABEL = "Couldn't restore the previous conversation";
/** @internal */
export const isSessionRestoredMarker = (m: ChatMessage): boolean =>
  m.role === 'system-event' && m.content === SESSION_RESTORED_LABEL;
/** @internal */
export const isSessionRestoreFailedMarker = (m: ChatMessage): boolean =>
  m.role === 'system-event' && m.content === SESSION_RESTORE_FAILED_LABEL;
/** Either restore notice — both display-only, stripped before persist. @internal */
export const isSessionNoticeMarker = (m: ChatMessage): boolean =>
  isSessionRestoredMarker(m) || isSessionRestoreFailedMarker(m);

/**
 * Toggle state that drives which messages appear in the scrolling list. Mirrors
 * the cog-menu switches; `showAgentSwitchIndicator` is pre-resolved by the
 * caller (it falls back to `showToolCalls` when the host hasn't configured an
 * explicit agent-switch toggle).
 */
export interface MessageFilterState {
  showToolCalls: boolean;
  showThinkingSteps: boolean;
  showNarration: boolean;
  showAgentSwitchIndicator: boolean;
  /**
   * Whether session-restore notices show — driven by the persistence ("remember
   * chat") toggle, since restore is that feature's concern (GENC-1351 §6). Kept
   * separate from `showAgentSwitchIndicator` so those two unrelated switches don't
   * cross-control each other.
   */
  showRestoreNotices: boolean;
}

/**
 * The trailing interaction to render in a pinned slot, separate from the
 * filtered list. Empty unless the most recent message carries an interaction.
 *
 * Pinning the trailing interaction keeps its widget out of the list's
 * reconciliation: FAST's `repeat` has no key function and reconciles by array
 * index, so re-filtering the list (a cog-menu toggle) would otherwise rebind an
 * existing row to a different message and re-create the live widget — discarding
 * any in-progress input. The slot's binding reads only `messages` and returns
 * the same message object across toggles, so the widget's DOM is preserved.
 *
 * It is the *trailing* message (resolved or not), not "unresolved only", so that
 * answering an interaction leaves it in the slot — `resolved` is forwarded in
 * place with no re-render. It rejoins the list naturally once a newer message
 * arrives and it is no longer last.
 */
export function trailingInteractionRow(messages: ChatMessage[]): ChatMessage[] {
  const last = messages[messages.length - 1];
  return last?.interaction ? [last] : [];
}

/**
 * Filters messages for the scrolling list per the current cog-menu toggles.
 *
 * The trailing interaction (see `trailingInteractionRow`) must be removed
 * before calling, as it is rendered separately.
 */
export function filterVisibleMessages(
  messages: ChatMessage[],
  toggles: MessageFilterState,
): ChatMessage[] {
  return messages.filter((m) => {
    // Compaction dividers are always shown — a real conversation landmark, not
    // optional UI noise (GENC-1351 §5.7).
    if (m.role === 'compacted-summary') {
      return true;
    }
    if (m.role === 'system-event') {
      // Restore notices belong to persistence ("remember chat"), agent-switch
      // indicators to their own toggle — gate each by its own switch.
      return isSessionNoticeMarker(m)
        ? toggles.showRestoreNotices
        : toggles.showAgentSwitchIndicator;
    }
    // Tool result messages are never shown to the user.
    if (m.role === 'tool') {
      return false;
    }
    // Reasoning (chain-of-thought summary) follows the thinking toggle.
    if (m.category === 'reasoning') {
      return toggles.showThinkingSteps;
    }
    // Narration (interstitial prose alongside a tool call) follows its own toggle.
    if (m.category === 'narration') {
      return toggles.showNarration;
    }
    // Legacy: sessions persisted before the reasoning/narration split tagged reasoning-ish
    // content with `thinking`; keep honouring the flag so their history still hides it.
    if (m.thinking && !toggles.showThinkingSteps) {
      return false;
    }
    // Drop empty assistant messages that slipped through.
    if (m.role === 'assistant' && !m.content?.trim() && !m.toolCalls?.length && !m.interaction) {
      return false;
    }
    const isToolRelated = !!(m.toolCalls?.length || m.toolResult);
    return !isToolRelated || toggles.showToolCalls;
  });
}
