// #region AssistantFirstMessageIntegrationTest:Module [TYPE Function]
// @SEMANTICS: assistant, integration-test, optimistic-message, conversation-race
// @PURPOSE: Verify first optimistic user message stays visible while a new conversation request is pending.
// @LAYER UI (Tests)
// @RELATION DEPENDS_ON -> [EXT:frontend:AssistantChatPanel]
// @INVARIANT: Starting a new conversation must not trigger history reload that overwrites the first local user message.

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import AssistantChatPanel from '../AssistantChatPanel.svelte';
import * as assistantApi from '$lib/api/assistant';

const { assistantState } = vi.hoisted(() => ({
      assistantState: (() => {
    let value = {
      isOpen: true,
      conversationId: null,
      datasetReviewSessionId: 'session-1',
      seedMessage: '',
      focusTarget: null,
    };
    const subscribers = new Set();
    return {
      subscribe(fn) {
        subscribers.add(fn);
        fn(value);
        return () => subscribers.delete(fn);
      },
      set(next) {
        value = next;
        subscribers.forEach((fn) => fn(value));
      },
      update(updater) {
        value = updater(value);
        subscribers.forEach((fn) => fn(value));
      },
    };
  })(),
}));

vi.mock('$lib/api/assistant.js', () => ({
  getAssistantHistory: vi.fn(() =>
    Promise.resolve({ items: [], total: 0, has_next: false }),
  ),
  getAssistantConversations: vi.fn(() =>
    Promise.resolve({
      items: [],
      total: 0,
      has_next: false,
      active_total: 0,
      archived_total: 0,
    }),
  ),
  deleteAssistantConversation: vi.fn(),
  confirmAssistantOperation: vi.fn(),
  cancelAssistantOperation: vi.fn(),
  sendAssistantMessage: vi.fn(),
}));

vi.mock('$lib/api', () => ({
  api: {
    getLlmStatus: vi.fn(() => Promise.resolve({ configured: true, reason: 'ok' })),
    fetchApi: vi.fn((endpoint) => {
      if (endpoint === '/dataset-orchestration/sessions/session-1/clarification') {
        return Promise.resolve(null);
      }
      if (endpoint === '/dataset-orchestration/sessions/session-1') {
        return Promise.resolve({ session_id: 'session-1', session_version: 2, version: 2 });
      }
      return Promise.resolve(null);
    }),
  },
  requestApi: vi.fn(),
}));

vi.mock('$lib/stores/assistantChat.svelte.js', () => ({
  assistantChatStore: {
    subscribe: assistantState.subscribe,
    get value() { return { isOpen: true, conversationId: null, datasetReviewSessionId: 'session-1', seedMessage: '', focusTarget: null }; },
  },
  closeAssistantChat: vi.fn(),
  setAssistantConversationId: vi.fn((conversationId) => {
    assistantState.update((state) => ({ ...state, conversationId }));
  }),
  setAssistantSeedMessage: vi.fn((seedMessage) => {
    assistantState.update((state) => ({ ...state, seedMessage }));
  }),
  setAssistantFocusTarget: vi.fn((focusTarget) => {
    assistantState.update((state) => ({ ...state, focusTarget }));
  }),
}));

vi.mock('$lib/toasts.svelte.js', () => ({
  addToast: vi.fn(),
}));

vi.mock('$lib/stores/taskDrawer.svelte.js', () => ({
  openDrawerForTask: vi.fn(),
}));

vi.mock('$app/navigation', () => ({
  goto: vi.fn(),
}));

vi.mock('../../../../services/gitService.js', () => ({
  gitService: {
    getDiff: vi.fn(),
  },
}));

const mockTranslations = {
  assistant: {
    title: 'Assistant',
    close: 'Close',
    conversations: 'Conversations',
    new: 'New',
    active: 'Active',
    archived: 'Archived',
    more: 'More',
    loading_history: 'Loading history',
    loading_older: 'Loading older',
    try_commands: 'Try commands',
    sample_command_branch: 'branch',
    sample_command_migration: 'migration',
    sample_command_status: 'status',
    you: 'You',
    assistant: 'Assistant',
    thinking: 'Thinking',
    input_placeholder: 'Type command',
    send: 'Send',
    conversation: 'Conversation',
    session_scope_title: 'Active review context',
    session_scope_label: 'Session',
    focus_target_label: 'Focus',
    clear_focus_action: 'Clear focus',
    states: {
      success: 'Success',
      failed: 'Failed',
      started: 'Started',
    },
    open_task_drawer: 'Open task drawer',
  },
  dashboard: {},
};
vi.mock('$lib/i18n/index.svelte.js', () => ({
  t: {
    subscribe: (fn) => {
      fn(mockTranslations);
      return () => {};
    },
  },
  getT: vi.fn(() => mockTranslations),
  _: vi.fn((key) => key),
}));

// #region assistant_first_message_tests:Function [TYPE Function]
// @RELATION BINDS_TO -> [EXT:frontend:AssistantFirstMessageIntegrationTest]
// @PURPOSE: Guard optimistic first-message UX against history reload race in new conversation flow.
// @PRE: Assistant panel renders with open state and mocked network dependencies.
// @POST: First user message remains visible before pending send request resolves.
describe('AssistantChatPanel first message flow', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    assistantState.set({
      isOpen: true,
      conversationId: null,
      datasetReviewSessionId: 'session-1',
      seedMessage: '',
      focusTarget: null,
    });
  });

  it('keeps the first optimistic user message visible during pending send in a new conversation', async () => {
    let resolveSend;
    assistantApi.sendAssistantMessage.mockImplementation(
      (payload) =>
        new Promise((resolve) => {
          resolveSend = () =>
            resolve({
              response_id: 'resp-1',
              conversation_id: payload.conversation_id,
              text: 'Started',
              state: 'success',
              created_at: new Date().toISOString(),
              actions: [],
            });
        }),
    );

    render(AssistantChatPanel);

    await waitFor(() => {
      expect(assistantApi.getAssistantHistory).toHaveBeenCalledTimes(1);
    });

    await fireEvent.click(screen.getByText('New'));

    const input = screen.getByPlaceholderText('Type command');
    await fireEvent.input(input, { target: { value: 'first prompt' } });
    await fireEvent.click(screen.getByText('Send'));

    expect(screen.getByText('first prompt')).toBeTruthy();
    expect(assistantApi.sendAssistantMessage).toHaveBeenCalledTimes(1);

    await waitFor(() => {
      expect(assistantApi.getAssistantHistory).toHaveBeenCalledTimes(1);
    });

    resolveSend();

    await waitFor(() => {
      expect(screen.getByText('Started')).toBeTruthy();
    });
  });

  it('passes dataset review session id with first assistant message', async () => {
    assistantApi.sendAssistantMessage.mockResolvedValue({
      response_id: 'resp-2',
      conversation_id: 'conv-2',
      text: 'Started',
      state: 'success',
      created_at: new Date().toISOString(),
      actions: [],
    });

    render(AssistantChatPanel);

    await waitFor(() => {
      expect(assistantApi.getAssistantHistory).toHaveBeenCalledTimes(1);
    });

    const input = screen.getByPlaceholderText('Type command');
    await fireEvent.input(input, { target: { value: 'review this mapping' } });
    await fireEvent.click(screen.getByText('Send'));

    await waitFor(() => {
      expect(assistantApi.sendAssistantMessage).toHaveBeenCalledWith(
        expect.objectContaining({
          dataset_review_session_id: 'session-1',
          message: 'review this mapping',
        }),
      );
    });
  });
});
// #endregion assistant_first_message_tests:Function
// #endregion AssistantFirstMessageIntegrationTest:Module
