/**
 * 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 SavedQueryList from '.';

// Increase default timeout
jest.setTimeout(30000);

const mockQueries = [...new Array(3)].map((_, i) => ({
  created_by: { id: i, first_name: 'user', last_name: `${i}` },
  created_on: `${i}-2020`,
  database: { database_name: `db ${i}`, id: i },
  changed_on_delta_humanized: '1 day ago',
  db_id: i,
  description: `SQL for ${i}`,
  id: i,
  label: `query ${i}`,
  schema: 'public',
  sql: `SELECT ${i} FROM table`,
  sql_tables: [{ catalog: null, schema: null, table: `${i}` }],
  tags: [],
}));

const mockUser = {
  userId: 1,
  firstName: 'admin',
  lastName: 'admin',
};

const queriesInfoEndpoint = 'glob:*/api/v1/saved_query/_info*';
const queriesEndpoint = 'glob:*/api/v1/saved_query/?*';
const queryEndpoint = 'glob:*/api/v1/saved_query/*';
const permalinkEndpoint = 'glob:*/api/v1/sqllab/permalink';

fetchMock.get(queriesInfoEndpoint, {
  permissions: ['can_write', 'can_read', 'can_export'],
});

fetchMock.get(queriesEndpoint, {
  ids: [2, 0, 1],
  result: mockQueries,
  count: mockQueries.length,
});

fetchMock.post(permalinkEndpoint, {
  url: 'http://localhost/permalink',
});

fetchMock.delete(queryEndpoint, {});

const renderList = (props = {}, storeOverrides = {}) =>
  render(
    <MemoryRouter>
      <QueryParamProvider>
        <SavedQueryList user={mockUser} {...props} />
      </QueryParamProvider>
    </MemoryRouter>,
    {
      useRedux: true,
      store: configureStore([thunk])({
        user: {
          ...mockUser,
          roles: { Admin: [['can_write', 'SavedQuery']] },
        },
        ...storeOverrides,
      }),
    },
  );

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

  test('renders', async () => {
    renderList();
    expect(await screen.findByText('Saved queries')).toBeInTheDocument();
  });

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

  test('renders query information', async () => {
    renderList();

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

    // Wait for data to load
    await waitFor(() => {
      mockQueries.forEach(query => {
        expect(screen.getByText(query.label)).toBeInTheDocument();
        expect(
          screen.getByText(query.database.database_name),
        ).toBeInTheDocument();
        expect(screen.getAllByText(query.schema)[0]).toBeInTheDocument();
      });
    });
  });

  test('handles query deletion', async () => {
    renderList();

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

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

    // Confirm deletion
    const deleteInput = screen.getByTestId('delete-modal-input');
    fireEvent.change(deleteInput, { target: { value: 'DELETE' } });

    const confirmButton = screen.getByTestId('modal-confirm-button');
    fireEvent.click(confirmButton);

    // Verify API call
    await waitFor(() => {
      expect(fetchMock.calls(/saved_query\/0/, 'DELETE')).toHaveLength(1);
    });
  });

  test('handles search filtering', async () => {
    renderList();

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

    // Find and use search input
    const searchInput = screen.getByTestId('filters-search');
    fireEvent.change(searchInput, { target: { value: 'test query' } });
    fireEvent.keyDown(searchInput, { key: 'Enter' });

    // Verify API call
    await waitFor(() => {
      const calls = fetchMock.calls(/saved_query\/\?q/);
      expect(calls.length).toBeGreaterThan(0);
      const lastCall = calls[calls.length - 1][0];
      expect(lastCall).toContain('order_column');
      expect(lastCall).toContain('page');
    });
  });

  test('fetches data', async () => {
    renderList();
    await waitFor(() => {
      const calls = fetchMock.calls(/saved_query\/\?q/);
      expect(calls).toHaveLength(1);
      expect(calls[0][0]).toContain('order_column');
      expect(calls[0][0]).toContain('page');
    });
  });

  test('handles sorting', async () => {
    renderList();

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

    // Find and click sort header
    const sortHeaders = screen.getAllByTestId('sort-header');
    fireEvent.click(sortHeaders[0]);

    // Verify API call includes sorting
    await waitFor(() => {
      const calls = fetchMock.calls(/saved_query\/\?q/);
      const lastCall = calls[calls.length - 1][0];
      const url = new URL(lastCall);
      const params = new URLSearchParams(url.search);
      const qParam = params.get('q');
      expect(qParam).toContain('order_column:label');
    });
  });

  test('shows/hides elements based on permissions', async () => {
    // Mock info response without write permission
    fetchMock.get(
      queriesInfoEndpoint,
      { permissions: ['can_read'] },
      { overwriteRoutes: true },
    );

    // Mock list response
    fetchMock.get(
      queriesEndpoint,
      { result: mockQueries, count: mockQueries.length },
      { overwriteRoutes: true },
    );

    renderList();

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

    // Wait for data to load
    await waitFor(() => {
      expect(screen.getByText(mockQueries[0].label)).toBeInTheDocument();
    });

    // Verify delete buttons are not shown
    expect(screen.queryByTestId('delete-action')).not.toBeInTheDocument();
  });
});
