/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import { describe, it, expect, vi, afterEach } from 'vitest';
import { renderWithProviders } from '../../test-utils/render.js';
import { ToastDisplay, shouldShowToast } from './ToastDisplay.js';
import { TransientMessageType } from '../../utils/events.js';
import { type UIState } from '../contexts/UIStateContext.js';
import { type InputState } from '../contexts/InputContext.js';
import { type TextBuffer } from './shared/text-buffer.js';
import { type HistoryItem } from '../types.js';

const renderToastDisplay = async (
  uiState: Partial<UIState> = {},
  inputState: Partial<InputState> = {},
) =>
  renderWithProviders(<ToastDisplay />, {
    uiState: {
      history: [] as HistoryItem[],
      ...uiState,
    },
    inputState: {
      buffer: { text: '' } as TextBuffer,
      showEscapePrompt: false,
      ...inputState,
    },
  });

describe('ToastDisplay', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  describe('shouldShowToast', () => {
    const baseUIState: Partial<UIState> = {
      ctrlCPressedOnce: false,
      transientMessage: null,
      ctrlDPressedOnce: false,
      history: [] as HistoryItem[],
      queueErrorMessage: null,
      showIsExpandableHint: false,
    };

    const baseInputState: Partial<InputState> = {
      showEscapePrompt: false,
      buffer: { text: '' } as TextBuffer,
    };

    it('returns false for default state', () => {
      expect(
        shouldShowToast(baseUIState as UIState, baseInputState as InputState),
      ).toBe(false);
    });

    it('returns true when showIsExpandableHint is true', () => {
      expect(
        shouldShowToast(
          {
            ...baseUIState,
            showIsExpandableHint: true,
          } as UIState,
          baseInputState as InputState,
        ),
      ).toBe(true);
    });

    it('returns true when ctrlCPressedOnce is true', () => {
      expect(
        shouldShowToast(
          { ...baseUIState, ctrlCPressedOnce: true } as UIState,
          baseInputState as InputState,
        ),
      ).toBe(true);
    });

    it('returns true when transientMessage is present', () => {
      expect(
        shouldShowToast(
          {
            ...baseUIState,
            transientMessage: { text: 'test', type: TransientMessageType.Hint },
          } as UIState,
          baseInputState as InputState,
        ),
      ).toBe(true);
    });

    it('returns true when ctrlDPressedOnce is true', () => {
      expect(
        shouldShowToast(
          { ...baseUIState, ctrlDPressedOnce: true } as UIState,
          baseInputState as InputState,
        ),
      ).toBe(true);
    });

    it('returns true when showEscapePrompt is true and buffer is NOT empty', () => {
      expect(
        shouldShowToast(
          {
            ...baseUIState,
          } as UIState,
          {
            ...baseInputState,
            showEscapePrompt: true,
            buffer: { text: 'some text' } as TextBuffer,
          } as InputState,
        ),
      ).toBe(true);
    });

    it('returns true when showEscapePrompt is true and history is NOT empty', () => {
      expect(
        shouldShowToast(
          {
            ...baseUIState,
            history: [{ id: '1' } as unknown as HistoryItem],
          } as UIState,
          {
            ...baseInputState,
            showEscapePrompt: true,
          } as InputState,
        ),
      ).toBe(true);
    });

    it('returns false when showEscapePrompt is true but buffer and history are empty', () => {
      expect(
        shouldShowToast(
          {
            ...baseUIState,
          } as UIState,
          {
            ...baseInputState,
            showEscapePrompt: true,
          } as InputState,
        ),
      ).toBe(false);
    });

    it('returns true when queueErrorMessage is present', () => {
      expect(
        shouldShowToast(
          {
            ...baseUIState,
            queueErrorMessage: 'error',
          } as UIState,
          baseInputState as InputState,
        ),
      ).toBe(true);
    });
  });

  it('renders nothing by default', async () => {
    const { lastFrame } = await renderToastDisplay();
    expect(lastFrame({ allowEmpty: true })).toBe('');
  });

  it('renders Ctrl+C prompt', async () => {
    const { lastFrame } = await renderToastDisplay({
      ctrlCPressedOnce: true,
    });
    expect(lastFrame()).toMatchSnapshot();
  });

  it('renders warning message', async () => {
    const { lastFrame } = await renderToastDisplay({
      transientMessage: {
        text: 'This is a warning',
        type: TransientMessageType.Warning,
      },
    });
    expect(lastFrame()).toMatchSnapshot();
  });

  it('renders hint message', async () => {
    const { lastFrame } = await renderToastDisplay({
      transientMessage: {
        text: 'This is a hint',
        type: TransientMessageType.Hint,
      },
    });
    expect(lastFrame()).toMatchSnapshot();
  });

  it('renders Ctrl+D prompt', async () => {
    const { lastFrame } = await renderToastDisplay({
      ctrlDPressedOnce: true,
    });
    expect(lastFrame()).toMatchSnapshot();
  });

  it('renders Escape prompt when buffer is empty', async () => {
    const { lastFrame } = await renderToastDisplay(
      {
        history: [{ id: 1, type: 'user', text: 'test' }] as HistoryItem[],
      },
      {
        showEscapePrompt: true,
      },
    );
    expect(lastFrame()).toMatchSnapshot();
  });

  it('renders Escape prompt when buffer is NOT empty', async () => {
    const { lastFrame } = await renderToastDisplay(
      {},
      {
        showEscapePrompt: true,
        buffer: { text: 'some text' } as TextBuffer,
      },
    );
    expect(lastFrame()).toMatchSnapshot();
  });

  it('renders Queue Error Message', async () => {
    const { lastFrame } = await renderToastDisplay({
      queueErrorMessage: 'Queue Error',
    });
    expect(lastFrame()).toMatchSnapshot();
  });

  it('renders expansion hint when showIsExpandableHint is true', async () => {
    const { lastFrame } = await renderToastDisplay({
      showIsExpandableHint: true,
      constrainHeight: true,
    });
    expect(lastFrame()).toContain(
      'Press Ctrl+O to show more lines of the last response',
    );
  });

  it('renders collapse hint when showIsExpandableHint is true and constrainHeight is false', async () => {
    const { lastFrame } = await renderToastDisplay({
      showIsExpandableHint: true,
      constrainHeight: false,
    });
    expect(lastFrame()).toContain(
      'Ctrl+O to collapse lines of the last response',
    );
  });
});
