/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';
import fetchMock from 'fetch-mock';
import {
  render,
  screen,
  fireEvent,
  waitFor,
} from 'spec/helpers/testing-library';
import { MemoryRouter } from 'react-router-dom';
import { QueryParamProvider } from 'use-query-params';

import AnnotationLayersList from 'src/pages/AnnotationLayerList';

const mockStore = configureStore([thunk]);
const store = mockStore({});

const layersInfoEndpoint = 'glob:*/api/v1/annotation_layer/_info*';
const layersEndpoint = 'glob:*/api/v1/annotation_layer/?*';
const layerEndpoint = 'glob:*/api/v1/annotation_layer/*';
const layersRelatedEndpoint = 'glob:*/api/v1/annotation_layer/related/*';

const mocklayers = [...new Array(3)].map((_, i) => ({
  changed_on_delta_humanized: `${i} day(s) ago`,
  created_by: {
    first_name: `user`,
    last_name: `${i}`,
  },
  created_on: new Date().toISOString,
  changed_on: new Date().toISOString,
  id: i,
  name: `layer ${i}`,
  desc: 'layer description',
}));

const mockUser = {
  userId: 1,
};

fetchMock.get(layersInfoEndpoint, {
  permissions: ['can_write'],
});
fetchMock.get(layersEndpoint, {
  result: mocklayers,
  layers_count: 3,
});

fetchMock.delete(layerEndpoint, {});
fetchMock.delete(layersEndpoint, {});

fetchMock.get(layersRelatedEndpoint, {
  created_by: {
    count: 0,
    result: [],
  },
});

const renderAnnotationLayersList = (props = {}) =>
  render(
    <MemoryRouter>
      <QueryParamProvider>
        <AnnotationLayersList user={mockUser} {...props} />
      </QueryParamProvider>
    </MemoryRouter>,
    {
      useRedux: true,
      store,
    },
  );

// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks
describe('AnnotationLayersList', () => {
  beforeEach(() => {
    fetchMock.resetHistory();
  });

  test('renders', async () => {
    renderAnnotationLayersList();
    expect(await screen.findByText('Annotation layers')).toBeInTheDocument();
  });

  test('renders a SubMenu', async () => {
    renderAnnotationLayersList();
    expect(await screen.findByRole('navigation')).toBeInTheDocument();
  });

  test('renders a ListView', async () => {
    renderAnnotationLayersList();
    expect(
      await screen.findByTestId('annotation-layers-list-view'),
    ).toBeInTheDocument();
  });

  test('renders a modal', async () => {
    renderAnnotationLayersList();
    const addButton = await screen.findByRole('button', {
      name: /annotation layer$/i,
    });
    fireEvent.click(addButton);
    expect(await screen.findByRole('dialog')).toBeInTheDocument();
  });

  test('fetches layers', async () => {
    renderAnnotationLayersList();
    await waitFor(() => {
      const calls = fetchMock.calls(/annotation_layer\/\?q/);
      expect(calls).toHaveLength(1);
      expect(calls[0][0]).toContain(
        'order_column:name,order_direction:desc,page:0,page_size:25',
      );
    });
  });

  test('renders Filters', async () => {
    renderAnnotationLayersList();
    await screen.findByTestId('annotation-layers-list-view');
    expect(screen.getByPlaceholderText(/type a value/i)).toBeInTheDocument();
  });

  test('searches', async () => {
    renderAnnotationLayersList();

    // Wait for list to load
    await screen.findByTestId('annotation-layers-list-view');

    // Find and fill search input
    const searchInput = screen.getByPlaceholderText(/type a value/i);
    fireEvent.change(searchInput, { target: { value: 'foo' } });
    fireEvent.keyDown(searchInput, { key: 'Enter', keyCode: 13 });

    // Wait for search API call
    await waitFor(() => {
      const calls = fetchMock.calls(/annotation_layer\/\?q/);
      const searchCall = calls.find(call =>
        call[0].includes('filters:!((col:name,opr:ct,value:foo))'),
      );
      expect(searchCall).toBeTruthy();
    });
  });

  test('deletes', async () => {
    renderAnnotationLayersList();

    // Wait for list to load
    await screen.findByTestId('annotation-layers-list-view');

    // Find and click delete button
    const deleteButtons = await screen.findAllByTestId('delete-action');
    fireEvent.click(deleteButtons[0]);

    // Check delete modal content
    const deleteModal = await screen.findByRole('dialog');
    expect(deleteModal).toHaveTextContent(/permanently delete the layer/i);

    // Type DELETE in confirmation input
    const deleteInput = await screen.findByTestId('delete-modal-input');
    fireEvent.change(deleteInput, { target: { value: 'DELETE' } });

    // Click confirm button
    const confirmButton = await screen.findByTestId('modal-confirm-button');
    fireEvent.click(confirmButton);

    // Wait for delete request
    await waitFor(() => {
      expect(fetchMock.calls(/annotation_layer\/0/, 'DELETE')).toHaveLength(1);
    });
  });

  test('shows bulk actions when bulk select is clicked', async () => {
    renderAnnotationLayersList();

    // Wait for list to load
    await screen.findByTestId('annotation-layers-list-view');

    // Click bulk select toggle
    const bulkSelectButton = screen.getByText(/bulk select/i);
    fireEvent.click(bulkSelectButton);

    // Wait for bulk select mode to be enabled
    await screen.findByText('0 Selected');
  }, 30000);
});
