import { expect, test } from '@/e2e/helper';
import { loadFixtures } from '@crates-io/msw/fixtures';
import { http, HttpResponse } from 'msw';

test.describe('Acceptance | crate page', { tag: '@acceptance' }, () => {
  test('visiting a crate page from the front page', async ({ page, msw }) => {
    let crate = await msw.db.crate.create({ name: 'nanomsg', newest_version: '0.6.1' });
    await msw.db.version.create({ crate, num: '0.6.1' });

    await page.goto('/');
    await page.click('[data-test-just-updated] [data-test-crate-link="0"]');

    await expect(page).toHaveURL('/crates/nanomsg/0.6.1');
    await expect(page).toHaveTitle('nanomsg - crates.io: Rust Package Registry');

    await expect(page.locator('[data-test-heading] [data-test-crate-name]')).toHaveText('nanomsg');
    await expect(page.locator('[data-test-heading] [data-test-crate-version]')).toHaveText('v0.6.1');
  });

  test('visiting /crates/nanomsg', async ({ page, msw, percy, a11y }) => {
    let crate = await msw.db.crate.create({ name: 'nanomsg' });
    await msw.db.version.create({ crate, num: '0.6.0' });
    await msw.db.version.create({ crate, num: '0.6.1', rust_version: '1.69' });

    await page.goto('/crates/nanomsg');

    await expect(page).toHaveURL('/crates/nanomsg');
    await expect(page).toHaveTitle('nanomsg - crates.io: Rust Package Registry');

    await expect(page.locator('[data-test-heading] [data-test-crate-name]')).toHaveText('nanomsg');
    await expect(page.locator('[data-test-heading] [data-test-crate-version]')).toHaveText('v0.6.1');
    await expect(page.locator('[data-test-crate-stats-label]')).toHaveText('Stats Overview');
    await expect(page.locator('[data-test-msrv]')).toContainText('v1.69.0');

    await percy.snapshot();
    await expect(page).toMatchAriaSnapshot({ name: 'aria-latest.yml' });
    await a11y.audit();
  });

  test('visiting /crates/nanomsg/', async ({ page, msw }) => {
    let crate = await msw.db.crate.create({ name: 'nanomsg' });
    await msw.db.version.create({ crate, num: '0.6.0' });
    await msw.db.version.create({ crate, num: '0.6.1' });

    await page.goto('/crates/nanomsg/');

    await expect(page).toHaveURL('/crates/nanomsg/');
    await expect(page).toHaveTitle('nanomsg - crates.io: Rust Package Registry');

    await expect(page.locator('[data-test-heading] [data-test-crate-name]')).toHaveText('nanomsg');
    await expect(page.locator('[data-test-heading] [data-test-crate-version]')).toHaveText('v0.6.1');
    await expect(page.locator('[data-test-crate-stats-label]')).toHaveText('Stats Overview');
  });

  test('visiting /crates/nanomsg/0.6.0', async ({ page, msw, percy, a11y }) => {
    let crate = await msw.db.crate.create({ name: 'nanomsg' });
    await msw.db.version.create({ crate, num: '0.6.0' });
    await msw.db.version.create({ crate, num: '0.6.1' });

    await page.goto('/crates/nanomsg/0.6.0');

    await expect(page).toHaveURL('/crates/nanomsg/0.6.0');
    await expect(page).toHaveTitle('nanomsg - crates.io: Rust Package Registry');

    await expect(page.locator('[data-test-heading] [data-test-crate-name]')).toHaveText('nanomsg');
    await expect(page.locator('[data-test-heading] [data-test-crate-version]')).toHaveText('v0.6.0');
    await expect(page.locator('[data-test-crate-stats-label]')).toHaveText('Stats Overview for 0.6.0 (see all)');

    await percy.snapshot();
    await expect(page).toMatchAriaSnapshot({ name: 'aria-version.yml' });
    await a11y.audit();
  });

  test('unknown crate shows an error message', async ({ page }) => {
    await page.goto('/crates/nanomsg');
    await expect(page).toHaveURL('/crates/nanomsg');
    await expect(page.locator('[data-test-404-page]')).toBeVisible();
    await expect(page.locator('[data-test-title]')).toHaveText(`Crate "nanomsg" not found`);
    await expect(page.locator('[data-test-go-back]')).toBeVisible();
    await expect(page.locator('[data-test-try-again]')).toHaveCount(0);
  });

  test('other crate loading error shows an error message', async ({ page, msw }) => {
    msw.worker.use(http.get('/api/v1/crates/:crate_name', () => HttpResponse.json({}, { status: 500 })));

    await page.goto('/crates/nanomsg');
    await expect(page).toHaveURL('/crates/nanomsg');
    await expect(page.locator('[data-test-404-page]')).toBeVisible();
    await expect(page.locator('[data-test-title]')).toHaveText(`Failed to load crate data`);
    await expect(page.locator('[data-test-go-back]')).toHaveCount(0);
    await expect(page.locator('[data-test-try-again]')).toBeVisible();
  });

  test('unknown versions fall back to latest version and show an error message', async ({ page, msw }) => {
    let crate = await msw.db.crate.create({ name: 'nanomsg' });
    await msw.db.version.create({ crate, num: '0.6.0' });
    await msw.db.version.create({ crate, num: '0.6.1' });

    await page.goto('/crates/nanomsg/0.7.0');

    await expect(page).toHaveURL('/crates/nanomsg/0.7.0');
    await expect(page.locator('[data-test-404-page]')).toBeVisible();
    await expect(page.locator('[data-test-title]')).toHaveText('nanomsg: Version 0.7.0 not found');
    await expect(page.locator('[data-test-go-back]')).toBeVisible();
    await expect(page.locator('[data-test-try-again]')).toHaveCount(0);
  });

  test('works for non-canonical names', async ({ page, msw }) => {
    let crate = await msw.db.crate.create({ name: 'foo-bar' });
    await msw.db.version.create({ crate });

    await page.goto('/crates/foo_bar');

    await expect(page).toHaveURL('/crates/foo_bar');
    await expect(page).toHaveTitle('foo-bar - crates.io: Rust Package Registry');

    await expect(page.locator('[data-test-heading] [data-test-crate-name]')).toHaveText('foo-bar');
  });

  test('navigating to the versions page', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    // default with a page size more than 13
    await page.goto('/crates/nanomsg');
    await page.click('[data-test-versions-tab] a');

    await expect(page.locator('[data-test-page-description]')).toHaveText(
      /13\s+of\s+13\s+nanomsg\s+versions since\s+December \d+th, 2014/,
    );
    await expect(page.locator('[data-test-keyword="network"]')).toBeVisible();
  });

  test('navigating to the versions page with custom per_page', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg/versions?per_page=10');
    await expect(page.locator('[data-test-page-description]')).toHaveText(
      /10\s+of\s+13\s+nanomsg\s+versions since\s+December \d+th, 2014/,
    );

    await page.getByTestId('load-more').click();
    await expect(page.locator('[data-test-page-description]')).toHaveText(
      /13\s+of\s+13\s+nanomsg\s+versions since\s+December \d+th, 2014/,
    );
  });

  test('navigating to the reverse dependencies page', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');
    await page.click('[data-test-rev-deps-tab] a');

    await expect(page).toHaveURL('/crates/nanomsg/reverse_dependencies');
    await expect(page.locator('a[href="/crates/unicorn-rpc"]')).toHaveText('unicorn-rpc');
    await expect(page.locator('[data-test-keyword="network"]')).toBeVisible();
  });

  test('navigating to the dependencies page', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');
    await page.click('[data-test-deps-tab] a');

    await expect(page).toHaveURL('/crates/nanomsg/0.6.1/dependencies');
    await expect(page.locator('[data-test-keyword="network"]')).toBeVisible();
  });

  test('navigating to the security page', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');
    await page.click('[data-test-security-tab] a');

    await expect(page).toHaveURL('/crates/nanomsg/security');
    await expect(page.locator('[data-test-keyword="network"]')).toBeVisible();
  });

  test('navigating to a user page', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');
    await page.click('[data-test-owners] [data-test-owner-link="blabaere"]');

    await expect(page).toHaveURL('/users/blabaere');
    await expect(page.locator('[data-test-heading] [data-test-username]')).toHaveText('blabaere');
  });

  test('navigating to a team page', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');
    await page.click('[data-test-owners] [data-test-owner-link="github:org:thehydroimpulse"]');

    await expect(page).toHaveURL('/teams/github:org:thehydroimpulse');
    await expect(page.locator('[data-test-heading] [data-test-team-name]')).toHaveText('thehydroimpulseteam');
  });

  test('crates having user-owners', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');

    await expect(
      page.locator('[data-test-owners] [data-test-owner-link="github:org:thehydroimpulse"] img'),
    ).toHaveAttribute('src', 'https://avatars.githubusercontent.com/u/565790?v=3&s=64');

    await expect(page.locator('[data-test-owners] li')).toHaveCount(4);
  });

  test('crates having team-owners', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');

    await expect(page.locator('[data-test-owners] [data-test-owner-link="github:org:thehydroimpulse"]')).toBeVisible();
    await expect(page.locator('[data-test-owners] li')).toHaveCount(4);
  });

  test('crates license is supplied by version', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');
    await expect(page.locator('[data-test-license]')).toHaveText(/License:\s*Apache-2\.0/);

    await page.goto('/crates/nanomsg/0.5.0');
    await expect(page.locator('[data-test-license]')).toHaveText(/License:\s*MIT OR Apache-2\.0/);
  });

  test('sidebar shows correct information', async ({ page, msw }) => {
    let crate = await msw.db.crate.create({ name: 'foo' });
    await msw.db.version.create({ crate, num: '0.5.0' });
    await msw.db.version.create({ crate, num: '1.0.0' });

    await page.goto('/crates/foo');
    await expect(page.locator('[data-test-linecounts]')).toHaveText('1,119 SLoC');

    await page.goto('/crates/foo/0.5.0');
    await expect(page.locator('[data-test-linecounts]')).toHaveText('520 SLoC');
  });

  test.skip('crates can be yanked by owner', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    let user = msw.db.user.findFirst(q => q.where({ login: 'thehydroimpulse' }));
    await msw.authenticateAs(user);

    await page.goto('/crates/nanomsg/0.5.0');
    let yankButton = page.locator('[data-test-version-yank-button="0.5.0"]');
    await yankButton.click();
    await expect(yankButton).toHaveText('Yanking...');
    await expect(yankButton).toBeDisabled();

    let unyankButton = page.locator('[data-test-version-unyank-button="0.5.0"]');
    await unyankButton.click();
    await expect(unyankButton).toHaveText('Unyanking...');
    await expect(unyankButton).toBeDisabled();

    await expect(yankButton).toBeVisible();
  });

  test('navigating to the owners page when not logged in', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');

    await expect(page.locator('[data-test-settings-tab]')).toHaveCount(0);
  });

  test('navigating to the owners page when not an owner', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    let user = msw.db.user.findFirst(q => q.where({ login: 'iain8' }));
    await msw.authenticateAs(user);

    await page.goto('/crates/nanomsg');

    await expect(page.locator('[data-test-settings-tab]')).toHaveCount(0);
  });

  test('navigating to the settings page', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    let user = msw.db.user.findFirst(q => q.where({ login: 'thehydroimpulse' }));
    await msw.authenticateAs(user);

    await page.goto('/crates/nanomsg');
    await page.click('[data-test-settings-tab] a');

    await expect(page).toHaveURL('/crates/nanomsg/settings');
    await expect(page.locator('[data-test-keyword="network"]')).toBeVisible();
  });

  test('keywords are shown when navigating from search', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/search?q=nanomsg');
    await page.getByRole('link', { name: 'nanomsg', exact: true }).click();

    await expect(page).toHaveURL('/crates/nanomsg');
    await expect(page.locator('[data-test-keyword]')).toBeVisible();
  });

  test('keywords are shown when navigating from crate to keywords, and then back to crate', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');
    await expect(page.locator('[data-test-keyword]')).toBeVisible();

    await page.getByRole('link', { name: 'network', exact: true }).click();
    await expect(page).toHaveURL('/keywords/network');
    await page.getByRole('link', { name: 'nanomsg', exact: true }).click();

    await expect(page).toHaveURL('/crates/nanomsg');
    await expect(page.locator('[data-test-keyword]')).toBeVisible();
  });

  test('keywords are shown when navigating from crate to searchs, and then back to crate', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg');
    await expect(page.locator('[data-test-keyword]')).toBeVisible();

    await page.fill('[data-test-search-input]', 'nanomsg');
    await page.locator('[data-test-search-form]').getByRole('button', { name: 'Search' }).click();
    await expect(page).toHaveURL('/search?q=nanomsg');
    await page.getByRole('link', { name: 'nanomsg', exact: true }).click();

    await expect(page).toHaveURL('/crates/nanomsg');
    await expect(page.locator('[data-test-keyword]')).toBeVisible();
  });

  test('keywords are shown on a version-specific page', async ({ page, msw }) => {
    await loadFixtures(msw.db);

    await page.goto('/crates/nanomsg/0.6.1');
    await expect(page.locator('[data-test-keyword="network"]')).toBeVisible();
  });
});
