// #region [EXT:frontend:AssistantChatTest]s [C:3] [TYPE Module] [SEMANTICS assistant, chat, store, test, session]
// @BRIEF Unit tests for assistant chat store validating open/close/toggle, conversation binding, session context, and seed message state transitions.
// @RELATION DEPENDS_ON -> [AssistantChatStore]

// #region [EXT:frontend:AssistantChatTest]:Module [TYPE Function]
// @SEMANTICS: test, store, assistant, toggle, conversation
// @PURPOSE: Validate assistant chat store visibility and conversation binding transitions.
// @LAYER UI (Tests)
// @RELATION DEPENDS_ON -> [EXT:frontend:assistantChat]
// @INVARIANT: Each test starts from default closed state.

import { describe, it, expect, beforeEach } from 'vitest';
import { get } from 'svelte/store';
import {
  assistantChatStore,
  closeAssistantChat,
  openAssistantChat,
  openAssistantChatWithContext,
  setAssistantConversationId,
  setAssistantDatasetReviewSessionId,
  setAssistantFocusTarget,
  setAssistantSeedMessage,
  toggleAssistantChat,
} from '../assistantChat.svelte.js';

// #region assistantChatStore_tests:Function [TYPE Function]
// @RELATION BINDS_TO -> [EXT:frontend:AssistantChatTest]]
// @PURPOSE: Group store unit scenarios for assistant panel behavior.
// @PRE: Store can be reset to baseline state in beforeEach hook.
// @POST: Open/close/toggle/conversation transitions are validated.
describe('assistantChatStore', () => {
  beforeEach(() => {
    closeAssistantChat();
  });

  it('should open assistant panel', () => {
    openAssistantChat();
    const state = get(assistantChatStore);
    expect(state.isOpen).toBe(true);
  });

  it('should close assistant panel', () => {
    openAssistantChat();
    closeAssistantChat();
    const state = get(assistantChatStore);
    expect(state.isOpen).toBe(false);
  });

  it('should toggle assistant panel state', () => {
    toggleAssistantChat();
    expect(get(assistantChatStore).isOpen).toBe(true);
    toggleAssistantChat();
    expect(get(assistantChatStore).isOpen).toBe(false);
  });

  it('should set conversation id', () => {
    setAssistantConversationId('conv-123');
    const state = get(assistantChatStore);
    expect(state.conversationId).toBe('conv-123');
  });

  it('should open assistant panel with dataset review context', () => {
    openAssistantChatWithContext({
      datasetReviewSessionId: 'session-42',
      seedMessage: 'Explain mapping warning',
      focusTarget: { target: 'mapping:m-1', source: 'test' },
    });
    const state = get(assistantChatStore);
    expect(state.isOpen).toBe(true);
    expect(state.datasetReviewSessionId).toBe('session-42');
    expect(state.seedMessage).toBe('Explain mapping warning');
    expect(state.focusTarget).toEqual({ target: 'mapping:m-1', source: 'test' });
  });

  it('should update dataset review session id and focus target independently', () => {
    setAssistantDatasetReviewSessionId('session-99');
    setAssistantFocusTarget({ target: 'finding:f-1', source: 'test' });
    const state = get(assistantChatStore);
    expect(state.datasetReviewSessionId).toBe('session-99');
    expect(state.focusTarget).toEqual({ target: 'finding:f-1', source: 'test' });
  });

  it('should set seed message', () => {
    setAssistantSeedMessage('Explain this warning');
    const state = get(assistantChatStore);
    expect(state.seedMessage).toBe('Explain this warning');
  });

  it('should set seed message to empty string when null passed', () => {
    setAssistantSeedMessage('Has content');
    setAssistantSeedMessage('');
    const state = get(assistantChatStore);
    expect(state.seedMessage).toBe('');
  });

  it('should open assistant panel with partial context preserving existing sessionId', () => {
    setAssistantDatasetReviewSessionId('session-existing');
    openAssistantChatWithContext({ seedMessage: 'Hello', focusTarget: null });
    const state = get(assistantChatStore);
    expect(state.isOpen).toBe(true);
    expect(state.seedMessage).toBe('Hello');
    // datasetReviewSessionId is preserved by ?? fallback
    expect(state.datasetReviewSessionId).toBe('session-existing');
  });

  it('should be a no-op calling openAssistantChat when already open', () => {
    openAssistantChat();
    expect(get(assistantChatStore).isOpen).toBe(true);
    // Call again — should be no-op (guard returns early)
    openAssistantChat();
    expect(get(assistantChatStore).isOpen).toBe(true);
  });

  it('should be a no-op calling closeAssistantChat when already closed', () => {
    // Already closed from beforeEach
    expect(get(assistantChatStore).isOpen).toBe(false);
    closeAssistantChat();
    expect(get(assistantChatStore).isOpen).toBe(false);
  });

  it('should not update conversation id when same value is set', () => {
    setAssistantConversationId('conv-1');
    const state1 = get(assistantChatStore);
    setAssistantConversationId('conv-1');
    const state2 = get(assistantChatStore);
    expect(state2.conversationId).toBe('conv-1');
    // State object shouldn't have changed reference
    expect(state2.conversationId).toBe(state1.conversationId);
  });

  it('should not update datasetReviewSessionId when same value is set', () => {
    setAssistantDatasetReviewSessionId('session-1');
    const state1 = get(assistantChatStore);
    setAssistantDatasetReviewSessionId('session-1');
    const state2 = get(assistantChatStore);
    expect(state2.datasetReviewSessionId).toBe('session-1');
    expect(state2.datasetReviewSessionId).toBe(state1.datasetReviewSessionId);
  });

  it('should set focus target to null via empty string coercion', () => {
    setAssistantFocusTarget('some-target');
    setAssistantFocusTarget('');
    const state = get(assistantChatStore);
    expect(state.focusTarget).toBeNull();
  });

  it('should toggle from closed to open then back to closed', () => {
    // Start closed (from beforeEach)
    expect(get(assistantChatStore).isOpen).toBe(false);
    toggleAssistantChat();
    expect(get(assistantChatStore).isOpen).toBe(true);
    toggleAssistantChat();
    expect(get(assistantChatStore).isOpen).toBe(false);
  });

  it('should handle unsubscribe from store', () => {
    const received: unknown[] = [];
    const unsub = assistantChatStore.subscribe((s) => received.push(s));
    openAssistantChat();
    expect(received.length).toBeGreaterThanOrEqual(2);
    unsub();
    openAssistantChat();
    const countAfterUnsub = received.length;
    toggleAssistantChat();
    // Should not have added more after unsub
    expect(received.length).toBe(countAfterUnsub);
  });

  // ── Edge cases for early-return guards ─────────────────────────

  it('should not update seedMessage when same value is set', () => {
    setAssistantSeedMessage('Hello');
    setAssistantSeedMessage('Hello'); // Same value — guard should return early
    const state = get(assistantChatStore);
    expect(state.seedMessage).toBe('Hello');
  });

  it('should not update focusTarget when same value is set', () => {
    setAssistantFocusTarget('target-1');
    setAssistantFocusTarget('target-1'); // Same value — guard should return early
    const state = get(assistantChatStore);
    expect(state.focusTarget).toBe('target-1');
  });

  it('should not update focusTarget when null is set after null', () => {
    // focusTarget starts as null
    setAssistantFocusTarget(null); // null === (null || null) → guard returns early
    const state = get(assistantChatStore);
    expect(state.focusTarget).toBeNull();
  });

  it('should not update datasetReviewSessionId when same value is set to null', () => {
    setAssistantDatasetReviewSessionId(null);
    setAssistantDatasetReviewSessionId(null); // Same null value — guard should return
    const state = get(assistantChatStore);
    expect(state.datasetReviewSessionId).toBeNull();
  });

  // ── value getter ──────────────────────────────────────────────

  it('assistantChatStore.value should return current state', () => {
    const state = assistantChatStore.value;
    expect(state).toHaveProperty('isOpen');
    expect(state).toHaveProperty('conversationId');
    expect(state).toHaveProperty('seedMessage');
  });

  // ── openAssistantChatWithContext edge cases ────────────────────

  it('should open with empty context preserving existing state', () => {
    setAssistantDatasetReviewSessionId('session-existing');
    openAssistantChatWithContext({});
    const state = get(assistantChatStore);
    expect(state.isOpen).toBe(true);
    expect(state.datasetReviewSessionId).toBe('session-existing');
    expect(state.seedMessage).toBe('');
    expect(state.focusTarget).toBeNull();
  });

  it('should open with only focusTarget preserving session', () => {
    setAssistantDatasetReviewSessionId('session-existing');
    openAssistantChatWithContext({ focusTarget: 'new-focus' });
    const state = get(assistantChatStore);
    expect(state.isOpen).toBe(true);
    expect(state.datasetReviewSessionId).toBe('session-existing');
    expect(state.focusTarget).toBe('new-focus');
  });
});
// #endregion assistantChatStore_tests:Function
// #endregion [EXT:frontend:AssistantChatTest]:Module
// #endregion [EXT:frontend:AssistantChatTest]s
