import React from 'react';
import Terminal, { LineType, ColorMode } from '../src/index';
import { render, fireEvent, screen, getByText  } from '@testing-library/react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-dom/test-utils';

jest.useFakeTimers();

describe('Terminal component', () => {
  let scrollIntoViewFn: (arg?: boolean | ScrollIntoViewOptions) => void;

  beforeAll(() => {
    scrollIntoViewFn = jest.fn();
    window.HTMLElement.prototype.scrollIntoView = scrollIntoViewFn;
  })

  test('Should render prompt', () => {
    const { container } = render(<Terminal lineData={ [] } onInput={ (input: string) => '' }/>);
    expect(container.querySelectorAll('span')).toHaveLength(1);
    expect(container.querySelector('span.react-terminal-line.react-terminal-active-input[data-terminal-prompt="$"]')).not.toBeNull();
    screen.getByPlaceholderText('Terminal Hidden Input');
  });

  test('Should not render prompt if onInput prop is null or not defined', () => {
    const { container } = render(<Terminal lineData={ [{type: LineType.Output, value: 'Some terminal output'}] } onInput={ null }/>);
    // Still renders output line...
    expect(container.querySelectorAll('span.react-terminal-line')).toHaveLength(1);
    // ... but not the prompt
    expect(container.querySelector('span.react-terminal-active-input')).toBeNull();
  });

  test('Should render line data', () => {
    const lineData = [
      {type: LineType.Input, value: 'Some terminal input'},
      {type: LineType.Output, value: 'Some terminal output'}
   ];
    const { container } = render(<Terminal lineData={ lineData } onInput={ (input: string) => '' }/>);
    expect(container.querySelectorAll('span')).toHaveLength(3);
    let renderedLine = screen.getByText('Some terminal output');
    expect(renderedLine.className).toEqual('react-terminal-line');
    renderedLine = screen.getByText('Some terminal input');
    expect(renderedLine.className).toEqual('react-terminal-line react-terminal-input');
  });

  test('Input prompt should not scroll into view when component first loads', () => {
    const lineData = [
      {type: LineType.Input, value: 'Some terminal input'},
      {type: LineType.Output, value: 'Some terminal output'}
   ];
    render(<Terminal lineData={ lineData } onInput={ (input: string) => '' }/>);
    jest.runAllTimers();
    expect(scrollIntoViewFn).not.toHaveBeenCalled();
  });

  test('Should accept input and scroll into view', () => {
    const onInput = jest.fn();
    const { rerender } = render(<Terminal lineData={ [] } onInput={ onInput }/>);
    const hiddenInput = screen.getByPlaceholderText('Terminal Hidden Input');
    fireEvent.change(hiddenInput, { target: { value: 'a' } });
    expect(screen.getByText('a').className).toEqual('react-terminal-line react-terminal-input react-terminal-active-input');
    screen.getByDisplayValue('a');
    expect(onInput.mock.calls.length).toEqual(0);
    fireEvent.keyDown(hiddenInput, { key: 'Enter', code: 'Enter' });
    expect(onInput).toHaveBeenCalledWith('a');
    rerender(<Terminal lineData={ [{type: LineType.Input, value: 'a'}] } onInput={ onInput }/>)
    jest.runAllTimers();
    expect(scrollIntoViewFn).toHaveBeenCalledTimes(1);
  });

  test('Should support changing color mode', () => {
    const { container } = render(<Terminal colorMode={ ColorMode.Light } lineData={ [] } onInput={ (input: string) => '' }/>);
    expect(container.querySelector('.react-terminal-wrapper.react-terminal-light')).not.toBeNull();
  });

  test('Should focus if onInput is defined', () => {
    const { container } = render(<Terminal lineData={ [] } onInput={ (input: string) => '' }/>)
    expect(container.ownerDocument.activeElement?.nodeName).toEqual('INPUT');
    expect(container.ownerDocument.activeElement?.className).toEqual('terminal-hidden-input');
  });

  test('Should not focus if onInput is undefined', () => {
    const { container } = render(<Terminal lineData={ [] } />)
    expect(container.ownerDocument.activeElement?.nodeName).toEqual('BODY');
  });

  test('Should take starting input value', () => {
    render(<Terminal lineData={ [] } onInput={ (input: string) => '' } startingInputValue="cat file.txt " />)
    const hiddenInput = screen.getByPlaceholderText('Terminal Hidden Input');
    const renderedLine = screen.getByText('cat file.txt');
    expect(renderedLine.className).toContain('react-terminal-line');
  });
});