import type { ChatMessage } from '@genesislcap/foundation-ai';
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
import { FoundationAiAssistant } from './main';

// Hold a reference so the custom-element registration isn't tree-shaken.
FoundationAiAssistant;

// GENC-1351 §5.11 (PR review): the single-element popout refactor removed the
// `claimSideEffect` first-writer latch, so the only thing preventing a pending
// interaction widget from being re-instantiated (and re-firing its side effect) on a
// dock/undock move is that the popout control is disabled while an interaction is
// pending — the template binds `?disabled=${x.hasActivePendingInteraction}`
// (`main.template.ts`). This suite pins the GATE LOGIC (`hasActivePendingInteraction`)
// that binding depends on, so a change to when the gate opens/closes is caught.
//
// Note: this deliberately does NOT mount the assistant. Mounting runs `connectedCallback`,
// which subscribes to `agenticActivityBus` — opening a cross-tab `BroadcastChannel` in a browser
// env and leaving the runner's event loop open. A getter-level test needs no mount and stays fast
// + reliable. `document.createElement` only upgrades the element (constructor) — it does not
// connect — so nothing subscribes, and the bus opens its channel lazily on first use, so none is
// ever created here (no teardown needed).

const Suite = createLogicSuite('FoundationAiAssistant popout interaction gate');

/** A fresh (unconnected) element with a fake session store wired in. */
function elementWith(state: string, messages: ChatMessage[]): FoundationAiAssistant {
  const el = document.createElement('foundation-ai-assistant') as FoundationAiAssistant;
  (el as unknown as { _sessionRef: unknown })._sessionRef = {
    store: { aiAssistant: { state, messages } },
  };
  return el;
}

const withInteraction = (resolved: boolean | { value: string }): ChatMessage[] => [
  { role: 'user', content: 'hi' } as ChatMessage,
  {
    role: 'assistant',
    content: resolved ? 'done' : '',
    interaction: { interactionId: '1', componentName: 'x', data: {}, resolved },
  } as unknown as ChatMessage,
];

Suite('is true only while a turn is running AND a pending interaction is the last message', () => {
  // Gated: a turn is running and the last message is an unresolved interaction. This is
  // the state during which the popout/dock control MUST be disabled.
  assert.is(
    elementWith('loading', withInteraction(false)).hasActivePendingInteraction,
    true,
    'busy + unresolved interaction → gated',
  );
  // `cancelling` counts as busy too (a stop was requested but the turn is winding down).
  assert.is(
    elementWith('cancelling', withInteraction(false)).hasActivePendingInteraction,
    true,
    'cancelling + unresolved interaction → gated',
  );
});

Suite('is false when idle, when the interaction is resolved, or when there is none', () => {
  // Not busy → never gated, even with an unresolved interaction present.
  assert.is(
    elementWith('idle', withInteraction(false)).hasActivePendingInteraction,
    false,
    'idle → not gated',
  );
  // Busy but the interaction has resolved → the widget is answered, nothing to strand.
  assert.is(
    elementWith('loading', withInteraction({ value: 'ok' })).hasActivePendingInteraction,
    false,
    'resolved interaction → not gated',
  );
  // Busy but the last message is not an interaction at all.
  assert.is(
    elementWith('loading', [{ role: 'assistant', content: 'thinking' } as ChatMessage])
      .hasActivePendingInteraction,
    false,
    'no interaction → not gated',
  );
  // No session / empty transcript.
  assert.is(
    elementWith('loading', []).hasActivePendingInteraction,
    false,
    'empty transcript → not gated',
  );
});

Suite.run();
