import React from 'react';

import '@testing-library/jest-dom';

import type { Router as RemixRouter } from '@remix-run/router';
import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import CreateImageWizard from '../../../Components/CreateImageWizardV2/CreateImageWizard';
import {
  clickBack,
  clickNext,
  renderCustomRoutesWithReduxRouter,
  verifyCancelButton,
} from '../../testUtils';

const routes = [
  {
    path: 'insights/image-builder/*',
    element: <div />,
  },
  {
    path: 'insights/image-builder/imagewizard/:composeId?',
    element: <CreateImageWizard />,
  },
];

// The router is just initiliazed here, it's assigned a value in the tests
let router: RemixRouter | undefined = undefined;

jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({
  useChrome: () => ({
    auth: {
      getUser: () => {
        return {
          identity: {
            internal: {
              org_id: 5,
            },
          },
        };
      },
    },
    isBeta: () => true,
    isProd: () => true,
    getEnvironment: () => 'prod',
  }),
}));

let mockContentSourcesEnabled: boolean;
jest.mock('@unleash/proxy-client-react', () => ({
  useUnleashContext: () => jest.fn(),
  useFlag: jest.fn((flag) => {
    switch (flag) {
      case 'image-builder.enable-content-sources':
        return mockContentSourcesEnabled;
      case 'image-builder.pkgrecs.enabled':
        return true;
      default:
        return false;
    }
  }),
}));

beforeAll(() => {
  // scrollTo is not defined in jsdom
  window.HTMLElement.prototype.scrollTo = function () {};
  mockContentSourcesEnabled = true;
});

afterEach(() => {
  jest.clearAllMocks();
  mockContentSourcesEnabled = true;
});

const typeIntoSearchBox = async (searchTerm: string) => {
  const searchbox = await screen.findByRole('textbox', {
    name: /search packages/i,
  });

  await userEvent.click(searchbox);
  await userEvent.type(searchbox, searchTerm);
};

const clearSearchBox = async () => {
  const searchbox = await screen.findByRole('textbox', {
    name: /search packages/i,
  });

  await userEvent.click(searchbox);
  await userEvent.clear(searchbox);
};

const getAllCheckboxes = async () => {
  const pkgTable = await screen.findByTestId('packages-table');
  await screen.findAllByTestId('package-row');

  const checkboxes = await within(pkgTable).findAllByRole('checkbox', {
    name: /select row/i,
  });

  return checkboxes;
};

const toggleSelected = async () => {
  await userEvent.click(
    await screen.findByRole('button', { name: /selected/i })
  );
};

const checkRecommendationsEmptyState = async () => {
  const recommendationButton = await screen.findByRole('button', {
    name: 'Recommended Red Hat packages',
  });

  await userEvent.click(recommendationButton);
  await screen.findByText('Select packages to generate recommendations.');
};

describe('Step Packages', () => {
  const setUp = async () => {
    mockContentSourcesEnabled = false;

    ({ router } = await renderCustomRoutesWithReduxRouter(
      'imagewizard',
      {},
      routes
    ));

    // select aws as upload destination
    await userEvent.click(await screen.findByTestId('upload-aws'));
    await clickNext();

    // aws step
    await userEvent.click(
      await screen.findByRole('radio', {
        name: /manually enter an account id\./i,
      })
    );
    await userEvent.type(
      await screen.findByRole('textbox', {
        name: 'aws account id',
      }),
      '012345678901'
    );
    await clickNext();
    // skip registration
    await screen.findByRole('textbox', {
      name: 'Select activation key',
    });

    await userEvent.click(
      await screen.findByTestId('registration-radio-later')
    );
    await clickNext();
    // skip OpenSCAP
    await clickNext();
    // skip snapshots
    await clickNext();
    // skip Repositories
    await clickNext();
    // skip fsc
    await clickNext();
  };

  test('clicking Next loads Image name', async () => {
    await setUp();

    await clickNext();

    await screen.findByRole('heading', {
      name: 'Details',
    });
  });

  test('clicking Back loads repositories', async () => {
    await setUp();

    await clickBack();

    await screen.findByRole('heading', {
      name: /Custom repositories/i,
    });
  });

  test('clicking Cancel loads landing page', async () => {
    await setUp();

    await verifyCancelButton(router);
  });

  test('should display search bar and toggle buttons', async () => {
    await setUp();

    await typeIntoSearchBox('test');

    await screen.findByRole('button', {
      name: /available/i,
    });
    await screen.findByRole('button', {
      name: /selected/i,
    });
  });

  test('should display default state', async () => {
    await setUp();

    await screen.findByText(
      'Search above to add additionalpackages to your image.'
    );
  });

  test('should display an exact match if found regardless of too many results', async () => {
    await setUp();

    await typeIntoSearchBox('testPkg-123');

    await screen.findByTestId('exact-match-row');

    await screen.findByRole('heading', {
      name: /too many results to display/i,
    });
  });

  test('search results should be sorted with most relevant results first', async () => {
    await setUp();

    await typeIntoSearchBox('test');

    const packagesTable = await screen.findByTestId('packages-table');

    const getRows = async () =>
      await within(packagesTable).findAllByTestId('package-row');
    const availablePackages = await getRows();

    await waitFor(() => expect(availablePackages).toHaveLength(6));

    expect(availablePackages[0]).toHaveTextContent('test');
    expect(availablePackages[1]).toHaveTextContent('test-sources');
    expect(availablePackages[2]).toHaveTextContent('testPkg');
    expect(availablePackages[3]).toHaveTextContent('testPkg-sources');
    expect(availablePackages[4]).toHaveTextContent('lib-test');
    expect(availablePackages[5]).toHaveTextContent('lib-test-sources');
  });

  test('selected packages are sorted the same way as available packages', async () => {
    await setUp();

    await typeIntoSearchBox('test');

    const checkboxes = await getAllCheckboxes();

    for (const checkbox in checkboxes) {
      await userEvent.click(checkboxes[checkbox]);
    }

    await toggleSelected();

    const packagesTable = await screen.findByTestId('packages-table');

    const getRows = async () =>
      await within(packagesTable).findAllByTestId('package-row');
    const availablePackages = await getRows();

    await waitFor(() => expect(availablePackages).toHaveLength(6));

    expect(availablePackages[0]).toHaveTextContent('test');
    expect(availablePackages[1]).toHaveTextContent('test-sources');
    expect(availablePackages[2]).toHaveTextContent('testPkg');
    expect(availablePackages[3]).toHaveTextContent('testPkg-sources');
    expect(availablePackages[4]).toHaveTextContent('lib-test');
    expect(availablePackages[5]).toHaveTextContent('lib-test-sources');
  });

  test('selected packages persist throughout steps', async () => {
    await setUp();

    await typeIntoSearchBox('test');
    const pkgTable = await screen.findByTestId('packages-table');
    await screen.findAllByTestId('package-row');

    const getFirstPkgCheckbox = async () =>
      await within(pkgTable).findByRole('checkbox', {
        name: /select row 0/i,
      });
    let firstPkgCheckbox = (await getFirstPkgCheckbox()) as HTMLInputElement;

    expect(firstPkgCheckbox.checked).toEqual(false);
    await userEvent.click(firstPkgCheckbox);
    expect(firstPkgCheckbox.checked).toEqual(true);

    await clickNext();
    await clickBack();

    firstPkgCheckbox = (await getFirstPkgCheckbox()) as HTMLInputElement;
    expect(firstPkgCheckbox.checked).toEqual(true);
  });

  test('should display empty available state on failed search', async () => {
    await setUp();

    await typeIntoSearchBox('asdf');

    await screen.findByText('No results found');
  });

  test('should display too many results state for more than 100 results', async () => {
    await setUp();

    await typeIntoSearchBox('te');

    await screen.findByText('Too many results to display');
  });

  test('should display relevant results in selected first', async () => {
    await setUp();

    await typeIntoSearchBox('test');

    const checkboxes = await getAllCheckboxes();

    await userEvent.click(checkboxes[0]);
    await userEvent.click(checkboxes[1]);

    await clearSearchBox();
    await typeIntoSearchBox('mock');

    // wait for debounce
    await waitFor(
      () => {
        expect(screen.getByText(/mockPkg/)).toBeInTheDocument();
      },
      {
        timeout: 1500,
      }
    );

    await userEvent.click(checkboxes[0]);
    await userEvent.click(checkboxes[1]);

    await toggleSelected();

    await clearSearchBox();
    await typeIntoSearchBox('test');

    const packagesTable = await screen.findByTestId('packages-table');

    const getRows = async () =>
      await within(packagesTable).findAllByTestId('package-row');
    const availablePackages = await getRows();

    expect(availablePackages[0]).toHaveTextContent('test');
    expect(availablePackages[1]).toHaveTextContent('test-sources');
  });

  test('should display recommendations', async () => {
    await setUp();

    await checkRecommendationsEmptyState();
    await typeIntoSearchBox('test');

    const checkboxes = await getAllCheckboxes();

    await userEvent.click(checkboxes[0]);

    await screen.findByText('recommendedPackage1');
    await screen.findByText('recommendedPackage2');
    await screen.findByText('recommendedPackage3');
  });

  test('allow to add recommendations to selected', async () => {
    await setUp();

    await checkRecommendationsEmptyState();

    const pkgTable = await screen.findByTestId('packages-table');

    await typeIntoSearchBox('test');

    const checkboxes = await getAllCheckboxes();

    await userEvent.click(checkboxes[0]);

    const addRecButtons = await screen.findAllByTestId(
      'add-recommendation-button'
    );

    await userEvent.click(addRecButtons[0]);

    await userEvent.click(
      await screen.findByRole('button', { name: /Selected/ })
    );

    await within(pkgTable).findByText('recommendedPackage1');
  });
});

describe('Step Custom repositories', () => {
  const user = userEvent.setup();
  const setUp = async () => {
    ({ router } = await renderCustomRoutesWithReduxRouter(
      'imagewizard',
      {},
      routes
    ));

    // select aws as upload destination
    await user.click(await screen.findByTestId('upload-aws'));
    await clickNext();

    // aws step
    await user.click(
      await screen.findByRole('radio', {
        name: /manually enter an account id\./i,
      })
    );
    await user.type(
      await screen.findByRole('textbox', {
        name: 'aws account id',
      }),
      '012345678901'
    );

    await clickNext();
    // skip registration
    await screen.findByRole('textbox', {
      name: 'Select activation key',
    });

    await user.click(await screen.findByLabelText('Register later'));
    await clickNext();
    // skip OpenSCAP
    await clickNext();
    // skip fsc
    await clickNext();
    // skip snapshots
    await clickNext();
  };

  test('selected repositories stored in and retrieved from form state', async () => {
    await setUp();

    const getFirstRepoCheckbox = async () =>
      await screen.findByRole('checkbox', {
        name: /select row 0/i,
      });
    let firstRepoCheckbox = (await getFirstRepoCheckbox()) as HTMLInputElement;

    expect(firstRepoCheckbox.checked).toEqual(false);
    await user.click(firstRepoCheckbox);
    expect(firstRepoCheckbox.checked).toEqual(true);

    await clickNext();
    await clickBack();

    firstRepoCheckbox = (await getFirstRepoCheckbox()) as HTMLInputElement;
    await waitFor(() => expect(firstRepoCheckbox.checked).toEqual(true));
  }, 30000);

  test('correct number of repositories is fetched', async () => {
    await setUp();

    await user.click(
      await screen.findByRole('button', {
        name: /^select$/i,
      })
    );

    await screen.findByText(/select all \(1015 items\)/i);
  });

  test('filter works', async () => {
    await setUp();

    await user.type(
      await screen.findByRole('textbox', { name: /search repositories/i }),
      '2zmya'
    );

    const table = await screen.findByTestId('repositories-table');
    const getRows = async () => await within(table).findAllByRole('row');

    let rows = await getRows();
    // remove first row from list since it is just header labels
    rows.shift();

    expect(rows).toHaveLength(1);

    // clear filter
    await user.click(await screen.findByRole('button', { name: /reset/i }));

    rows = await getRows();
    // remove first row from list since it is just header labels
    rows.shift();

    await waitFor(() => expect(rows).toHaveLength(10));
  }, 30000);

  test('press on Selected button to see selected repositories list', async () => {
    await setUp();

    const getFirstRepoCheckbox = async () =>
      await screen.findByRole('checkbox', {
        name: /select row 0/i,
      });
    const firstRepoCheckbox =
      (await getFirstRepoCheckbox()) as HTMLInputElement;

    expect(firstRepoCheckbox.checked).toEqual(false);
    await user.click(firstRepoCheckbox);
    expect(firstRepoCheckbox.checked).toEqual(true);

    const getSelectedButton = async () =>
      await screen.findByRole('button', {
        name: /selected repositories/i,
      });

    const selectedButton = await getSelectedButton();
    await user.click(selectedButton);

    expect(firstRepoCheckbox.checked).toEqual(true);

    await clickNext();
    await clickBack();
    await waitFor(() => expect(firstRepoCheckbox.checked).toEqual(true));
  });

  test('press on All button to see all repositories list', async () => {
    await setUp();

    const getFirstRepoCheckbox = async () =>
      await screen.findByRole('checkbox', {
        name: /select row 0/i,
      });
    const firstRepoCheckbox =
      (await getFirstRepoCheckbox()) as HTMLInputElement;

    const getSecondRepoCheckbox = async () =>
      await screen.findByRole('checkbox', {
        name: /select row 1/i,
      });
    const secondRepoCheckbox =
      (await getSecondRepoCheckbox()) as HTMLInputElement;

    expect(firstRepoCheckbox.checked).toEqual(false);
    expect(secondRepoCheckbox.checked).toEqual(false);
    await user.click(firstRepoCheckbox);
    expect(firstRepoCheckbox.checked).toEqual(true);
    expect(secondRepoCheckbox.checked).toEqual(false);

    const getAllButton = async () =>
      await screen.findByRole('button', {
        name: /all repositories/i,
      });

    const allButton = await getAllButton();
    await user.click(allButton);

    expect(firstRepoCheckbox.checked).toEqual(true);
    expect(secondRepoCheckbox.checked).toEqual(false);

    await clickNext();
    await clickBack();

    expect(firstRepoCheckbox.checked).toEqual(true);
    await waitFor(() => expect(secondRepoCheckbox.checked).toEqual(false));
  });

  test('press on Selected button to see selected repositories list at the second page and filter checked repo', async () => {
    await setUp();

    const getFirstRepoCheckbox = async () =>
      await screen.findByRole('checkbox', {
        name: /select row 0/i,
      });

    const firstRepoCheckbox =
      (await getFirstRepoCheckbox()) as HTMLInputElement;

    const getNextPageButton = async () =>
      await screen.findAllByRole('button', {
        name: /go to next page/i,
      });

    const nextPageButton = await getNextPageButton();

    expect(firstRepoCheckbox.checked).toEqual(false);
    await user.click(firstRepoCheckbox);
    expect(firstRepoCheckbox.checked).toEqual(true);

    await user.click(nextPageButton[0]);

    const getSelectedButton = async () =>
      await screen.findByRole('button', {
        name: /selected repositories/i,
      });

    const selectedButton = await getSelectedButton();
    await user.click(selectedButton);

    expect(firstRepoCheckbox.checked).toEqual(true);

    await user.type(
      await screen.findByRole('textbox', { name: /search repositories/i }),
      '13lk3'
    );

    expect(firstRepoCheckbox.checked).toEqual(true);

    await clickNext();
    clickBack();
    expect(firstRepoCheckbox.checked).toEqual(true);
    await user.click(firstRepoCheckbox);
    await waitFor(() => expect(firstRepoCheckbox.checked).toEqual(false));
  }, 30000);
});
//
// describe('On Recreate', () => {
//   const user = userEvent.setup();
//   const setUp = async () => {
//     ({ router } = renderWithReduxRouter(
//       'imagewizard/hyk93673-8dcc-4a61-ac30-e9f4940d8346'
//     ));
//   };
//
//   const setUpUnavailableRepo = async () => {
//     ({ router } = renderWithReduxRouter(
//       'imagewizard/b7193673-8dcc-4a5f-ac30-e9f4940d8346'
//     ));
//   };
//
//   test('with valid repositories', async () => {
//     await setUp();
//
//     await screen.findByRole('heading', { name: /review/i });
//     expect(
//       screen.queryByText('Previously added custom repository unavailable')
//     ).not.toBeInTheDocument();
//
//     const createImageButton = await screen.findByRole('button', {
//       name: /create image/i,
//     });
//     await waitFor(() => expect(createImageButton).toBeEnabled());
//
//     await user.click(
//       await screen.findByRole('button', { name: /custom repositories/i })
//     );
//
//     await screen.findByRole('heading', { name: /custom repositories/i });
//     expect(
//       screen.queryByText('Previously added custom repository unavailable')
//     ).not.toBeInTheDocument();
//
//     const table = await screen.findByTestId('repositories-table');
//
//     const { getAllByRole } = within(table);
//     const rows = getAllByRole('row');
//
//     const availableRepo = rows[1].cells[1];
//     expect(availableRepo).toHaveTextContent(
//       '13lk3http://yum.theforeman.org/releases/3.4/el8/x86_64/'
//     );
//
//     const availableRepoCheckbox = await screen.findByRole('checkbox', {
//       name: /select row 0/i,
//     });
//     expect(availableRepoCheckbox).toBeEnabled();
//   });
//
//   test('with repositories that are no longer available', async () => {
//     await setUpUnavailableRepo();
//
//     await screen.findByRole('heading', { name: /review/i });
//     await screen.findByText('Previously added custom repository unavailable');
//
//     const createImageButton = await screen.findByRole('button', {
//       name: /create image/i,
//     });
//     expect(createImageButton).toBeDisabled();
//
//     await user.click(
//       await screen.findByRole('button', { name: /custom repositories/i })
//     );
//
//     await screen.findByRole('heading', { name: /custom repositories/i });
//     await screen.findByText('Previously added custom repository unavailable');
//
//     const table = await screen.findByTestId('repositories-table');
//
//     const { getAllByRole } = within(table);
//     const rows = getAllByRole('row');
//
//     const unavailableRepo = rows[1].cells[1];
//     expect(unavailableRepo).toHaveTextContent(
//       'Repository with the following url is no longer available:http://unreachable.link.to.repo.org/x86_64/'
//     );
//
//     const unavailableRepoCheckbox = await screen.findByRole('checkbox', {
//       name: /select row 0/i,
//     });
//     expect(unavailableRepoCheckbox).toBeDisabled();
//   });
// });
//
