// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import { useState } from 'react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders, screen } from '../../../../test-utils/test-utils.tsx';
import GestureValueEditor from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/GestureValueEditor';

function TestHarness({ initialValue = '', initialType=true, placeholder = 'Gesture name' } : { initialValue?: string, initialType?: boolean, placeholder?: string }) {
  const [value, setValue] = useState(initialValue);
  const [_, setType] = useState(initialType)
  return (
    <GestureValueEditor value={value} setValue={setValue} setType={setType} placeholder={placeholder} />
  );
}

describe('GestureValueEditor', () => {
  let user: ReturnType<typeof userEvent.setup>;

  beforeEach(() => {
    user = userEvent.setup();
  });

  test('renders in tag mode by default and allows selecting a tag via button and select', async () => {
    renderWithProviders(<TestHarness />);

    // Tag selector should be present
    const select = screen.getByTestId('tagSelectorTestID') as HTMLSelectElement;
    expect(select).toBeInTheDocument();
    expect(select.value).toBe('');

    // Choose a tag via select
    await user.selectOptions(select, 'happy');
    expect(select.value).toBe('happy');

    // The corresponding tag button should reflect the selection (have the selected class)
    const happyButton = screen.getByRole('button', { name: /happy/i });
    expect(happyButton).toBeInTheDocument();
    expect(happyButton.className).toMatch(/selected/);
  });

  test('switches to single mode and shows suggestions list', async () => {
    renderWithProviders(<TestHarness initialValue={'happy'} />);

    const singleButton = screen.getByRole('button', { name: /^single$/i });
    await user.click(singleButton);

    // Input should be present with placeholder
    const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;
    expect(input).toBeInTheDocument();

    // Because switching to single populates suggestions, we expect at least one suggestion item
    const suggestion = await screen.findByText(/Listening_1/);
    expect(suggestion).toBeInTheDocument();
  });

  test('typing filters suggestions and selecting a suggestion commits the value and hides the list', async () => {
    renderWithProviders(<TestHarness />);

    // Switch to single mode
    await user.click(screen.getByRole('button', { name: /^single$/i }));
    const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;

    // Type a substring that matches some suggestions
    await user.type(input, 'Listening_2');

    // The suggestion should appear and include the text we typed
    const matching = await screen.findByText(/Listening_2/);
    expect(matching).toBeInTheDocument();

    // Click the suggestion
    await user.click(matching);

    // After selecting, input should contain that suggestion and suggestions should be hidden
    expect(input.value).toContain('Listening_2');
    expect(screen.queryByText(/Listening_1/)).toBeNull();
  });

  test('typing a non-matching string hides the suggestions list', async () => {
    renderWithProviders(<TestHarness />);
    await user.click(screen.getByRole('button', { name: /^single$/i }));
    const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;

    await user.type(input, 'no-match-zzz');

    // There should be no suggestion that includes that gibberish
    expect(screen.queryByText(/no-match-zzz/)).toBeNull();
  });

  test('switching back to tag mode clears value when it is not a valid tag and preserves it when it is', async () => {
    renderWithProviders(<TestHarness />);

    // Switch to single mode and pick a suggestion (which is not a semantic tag)
    await user.click(screen.getByRole('button', { name: /^single$/i }));
    const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;
    await user.type(input, 'Listening_3');
    const suggestion = await screen.findByText(/Listening_3/);
    await user.click(suggestion);

    // Switch back to tag mode -> value should be cleared (not in tag list)
    await user.click(screen.getByRole('button', { name: /^tag$/i }));
    const select = screen.getByTestId('tagSelectorTestID') as HTMLSelectElement;
    expect(select.value).toBe('');

    // Now pick a valid tag and switch to single then back to tag
    await user.selectOptions(select, 'happy');
    expect(select.value).toBe('happy');

    // Switch to single and then back to tag; since 'happy' is a valid tag, it should remain
    await user.click(screen.getByRole('button', { name: /^single$/i }));
    await user.click(screen.getByRole('button', { name: /^tag$/i }));
    expect(select.value).toBe('happy');
  });

  test('focus on input re-shows filtered suggestions when customValue is present', async () => {
    renderWithProviders(<TestHarness />);

    // Switch to single mode and type to filter
    await user.click(screen.getByRole('button', { name: /^single$/i }));
    const input = screen.getByPlaceholderText('Gesture name') as HTMLInputElement;

    await user.type(input, 'Listening_4');
    const found = await screen.findByText(/Listening_4/);
    expect(found).toBeInTheDocument();

    // Blur the input
    input.blur();
    expect(found).toBeInTheDocument();

    // Focus the input again and ensure the suggestions remain or reappear
    await user.click(input);
    const foundAgain = await screen.findByText(/Listening_4/);
    expect(foundAgain).toBeInTheDocument();
  });
});
