import type { ChatInteraction, ChatMessage } from '@genesislcap/foundation-ai';
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
import {
  filterVisibleMessages,
  type MessageFilterState,
  SESSION_RESTORED_LABEL,
  SESSION_RESTORE_FAILED_LABEL,
  trailingInteractionRow,
} from './message-partition';

const msg = (overrides: Partial<ChatMessage> = {}): ChatMessage => ({
  role: 'assistant',
  content: '',
  ...overrides,
});

const interaction = (interactionId: string, resolved = false): ChatInteraction => ({
  interactionId,
  componentName: 'test-widget',
  data: {},
  ...(resolved ? { resolved: { status: 'approved' } } : {}),
});

const ALL_ON: MessageFilterState = {
  showToolCalls: true,
  showThinkingSteps: true,
  showNarration: true,
  showAgentSwitchIndicator: true,
  showRestoreNotices: true,
};
const ALL_OFF: MessageFilterState = {
  showToolCalls: false,
  showThinkingSteps: false,
  showNarration: false,
  showAgentSwitchIndicator: false,
  showRestoreNotices: false,
};

const trailing = createLogicSuite('trailingInteractionRow');

trailing('is empty when there are no messages', () => {
  assert.equal(trailingInteractionRow([]), []);
});

trailing('is empty when the last message has no interaction', () => {
  assert.equal(trailingInteractionRow([msg({ content: 'hi' })]), []);
});

trailing('returns the last message when it carries an interaction (pending)', () => {
  const pending = msg({ interaction: interaction('id-a') });
  assert.equal(trailingInteractionRow([msg({ content: 'q' }), pending]), [pending]);
});

trailing('still returns the trailing interaction once it is resolved', () => {
  // Keeps it in the pinned slot so resolving forwards in place without a rebuild.
  const resolved = msg({ interaction: interaction('id-a', true) });
  assert.equal(trailingInteractionRow([resolved]), [resolved]);
});

trailing('returns nothing once a newer non-interaction message arrives', () => {
  // The interaction is no longer last, so it rejoins the scrolling list.
  const earlier = msg({ interaction: interaction('id-a', true) });
  assert.equal(trailingInteractionRow([earlier, msg({ content: 'next' })]), []);
});

trailing('returns the same message reference (lets FAST short-circuit rebind)', () => {
  const pending = msg({ interaction: interaction('id-a') });
  assert.is(trailingInteractionRow([pending])[0], pending);
});

const filter = createLogicSuite('filterVisibleMessages');

filter('hides tool result messages regardless of toggles', () => {
  const msgs = [msg({ role: 'tool', toolResult: { toolCallId: 't1', content: 'r' } })];
  assert.equal(filterVisibleMessages(msgs, ALL_ON), []);
});

filter('hides tool-call messages when showToolCalls is off, shows them when on', () => {
  const toolCall = msg({ toolCalls: [{ id: 't1', name: 'do', args: {} } as any] });
  assert.equal(filterVisibleMessages([toolCall], ALL_OFF), []);
  assert.equal(filterVisibleMessages([toolCall], ALL_ON), [toolCall]);
});

filter('hides thinking messages when showThinkingSteps is off', () => {
  const thinking = msg({ thinking: true, content: 'hmm' });
  assert.equal(filterVisibleMessages([thinking], ALL_OFF), []);
  assert.equal(filterVisibleMessages([thinking], ALL_ON), [thinking]);
});

filter('hides system-event rows unless the agent-switch toggle is on', () => {
  const event = msg({ role: 'system-event', content: 'switched' });
  assert.equal(filterVisibleMessages([event], ALL_OFF), []);
  assert.equal(filterVisibleMessages([event], ALL_ON), [event]);
});

filter('drops empty assistant messages', () => {
  assert.equal(filterVisibleMessages([msg({ content: '   ' })], ALL_ON), []);
});

filter('keeps user and non-empty assistant messages with everything off', () => {
  const user = msg({ role: 'user', content: 'hello' });
  const reply = msg({ content: 'hi there' });
  assert.equal(filterVisibleMessages([user, reply], ALL_OFF), [user, reply]);
});

// GENC-1351 §6: restore notices are gated by their own (persistence) toggle, NOT
// by showAgentSwitchIndicator — the two unrelated switches must not cross-control.
filter(
  'restore notices follow showRestoreNotices; agent-switch rows follow showAgentSwitchIndicator',
  () => {
    const restored = msg({ role: 'system-event', content: SESSION_RESTORED_LABEL });
    const failed = msg({ role: 'system-event', content: SESSION_RESTORE_FAILED_LABEL });
    const agentSwitch = msg({ role: 'system-event', content: 'Guided Booking' });
    const rows = [restored, failed, agentSwitch];

    // Persistence on, agent-switch off → only the restore notices show.
    assert.equal(filterVisibleMessages(rows, { ...ALL_OFF, showRestoreNotices: true }), [
      restored,
      failed,
    ]);
    // Agent-switch on, persistence off → only the agent-switch indicator shows.
    assert.equal(filterVisibleMessages(rows, { ...ALL_OFF, showAgentSwitchIndicator: true }), [
      agentSwitch,
    ]);
  },
);

trailing.run();
filter.run();
