import { test, expect, type Page } from '@playwright/test';

import { loginAsAdmin, loginAsUser } from './helpers/auth';

const TIMEOUT = 60 * 1000;
const VOTES_LIST_URL = '/votes';
const TASKS_GRADE_URL = '/tasks/grade';
const KNOWN_TASK_ID = 'abc422_a'; // From prisma/tasks.ts seed data
const KNOWN_VOTE_DETAIL_URL = '/votes/abc422_a'; // From prisma/tasks.ts seed data

// ---------------------------------------------------------------------------
// Votes list page (/votes)
// ---------------------------------------------------------------------------

test.describe('votes list page (/votes)', () => {
  test('unauthenticated user can view the page without redirect', async ({ page }) => {
    await page.goto(VOTES_LIST_URL);
    await expect(page).toHaveURL(VOTES_LIST_URL, { timeout: TIMEOUT });
    await expect(page.getByRole('heading', { name: '投票' })).toBeVisible({
      timeout: TIMEOUT,
    });
  });

  test('task table is visible to unauthenticated user', async ({ page }) => {
    await page.goto(VOTES_LIST_URL);
    await expect(page.getByRole('columnheader', { name: '問題名' })).toBeVisible({
      timeout: TIMEOUT,
    });
    await expect(page.getByRole('columnheader', { name: '出典' })).toBeVisible({
      timeout: TIMEOUT,
    });
  });

  test('logged-in user can view the page', async ({ page }) => {
    await loginAsUser(page);
    await page.goto(VOTES_LIST_URL);
    await expect(page).toHaveURL(VOTES_LIST_URL, { timeout: TIMEOUT });
    await expect(page.getByRole('heading', { name: '投票' })).toBeVisible({
      timeout: TIMEOUT,
    });
  });

  test('search input filters tasks by title', async ({ page }) => {
    await page.goto(VOTES_LIST_URL);
    const searchInput = page.getByPlaceholder('問題名・問題ID・出典で検索');
    await expect(searchInput).toBeVisible({ timeout: TIMEOUT });

    // Type a string unlikely to match any task to get 0 results
    await searchInput.fill('__no_match_expected__');
    await expect(page.getByText('該当する問題が見つかりませんでした')).toBeVisible({
      timeout: TIMEOUT,
    });
  });
});

// ---------------------------------------------------------------------------
// Vote detail page (/votes/[slug])
// ---------------------------------------------------------------------------

test.describe('vote detail page (/votes/[slug])', () => {
  /**
   * Navigates to the first task in the vote list by searching for a known task ID,
   * then clicking the first result link.
   * Callers must check page.url() and call test.skip if still on the list page.
   */
  async function navigateToFirstVoteDetailPage(page: Page): Promise<void> {
    await page.goto(VOTES_LIST_URL);
    await expect(page.getByRole('columnheader', { name: '問題名' })).toBeVisible({
      timeout: TIMEOUT,
    });

    // Fill search to render task rows (table body is empty until search !== '')
    const searchInput = page.getByPlaceholder('問題名・問題ID・出典で検索');
    await searchInput.fill(KNOWN_TASK_ID);

    const firstLink = page.locator('table').getByRole('link').first();
    const hasTask = await firstLink.isVisible();

    if (!hasTask) {
      return; // No matching tasks in DB — callers must call test.skip
    }

    await firstLink.click();
    await expect(page).toHaveURL(/\/votes\/.+/, { timeout: TIMEOUT });
  }

  test.describe('unauthenticated user', () => {
    test('can view the task detail page without redirect', async ({ page }) => {
      await navigateToFirstVoteDetailPage(page);
      const onDetailPage = /\/votes\/.+/.test(page.url());
      test.skip(!onDetailPage, 'no matching tasks in DB');
      // Should stay on /votes/[slug], not redirected
      await expect(page).toHaveURL(/\/votes\/.+/, { timeout: TIMEOUT });
    });

    test('sees login prompt instead of vote buttons', async ({ page }) => {
      await page.goto(KNOWN_VOTE_DETAIL_URL);

      const content = page.locator('.container');

      await expect(content.getByText('投票するにはログインが必要です')).toBeVisible({
        timeout: TIMEOUT,
      });
      await expect(content.getByRole('link', { name: 'ログイン' })).toBeVisible({
        timeout: TIMEOUT,
      });
      await expect(content.getByRole('link', { name: 'アカウント作成' })).toBeVisible({
        timeout: TIMEOUT,
      });
    });

    test('does not see vote grade buttons', async ({ page }) => {
      await page.goto(KNOWN_VOTE_DETAIL_URL);
      // Grade buttons are only rendered inside the vote form (not shown when logged out)
      await expect(page.locator('form[action="?/voteAbsoluteGrade"]')).not.toBeAttached();
    });

    test('breadcrumb link navigates back to /votes', async ({ page }) => {
      await page.goto(KNOWN_VOTE_DETAIL_URL);
      await page.locator('.container').locator('nav').getByRole('link', { name: '投票' }).click();
      await expect(page).toHaveURL(VOTES_LIST_URL, { timeout: TIMEOUT });
    });
  });

  test.describe('logged-in user', () => {
    test.beforeEach(async ({ page }) => {
      await loginAsUser(page);
    });

    test('can view the task detail page', async ({ page }) => {
      await page.goto(KNOWN_VOTE_DETAIL_URL);
      await expect(page).toHaveURL(/\/votes\/.+/, { timeout: TIMEOUT });
    });

    test('sees vote grade buttons', async ({ page }) => {
      await page.goto(KNOWN_VOTE_DETAIL_URL);

      // Skip if the test user is not AtCoder-verified.
      // Wait for either the vote form or the unverified message to appear before deciding.
      const voteForm = page.locator('form[action="?/voteAbsoluteGrade"]');
      const unverifiedMessage = page.getByText('AtCoderアカウントの認証が必要です');
      await expect(voteForm.or(unverifiedMessage)).toBeVisible({ timeout: TIMEOUT });
      const isUnverified = await unverifiedMessage.isVisible();
      test.skip(isUnverified, 'test user is not AtCoder-verified');

      // Explicit check: voteForm is already guaranteed visible by the or() wait above,
      // but this documents the expected state for verified users.
      await expect(voteForm).toBeVisible({
        timeout: TIMEOUT,
      });
      // The grade buttons should include Q11 (11Q)
      await expect(page.getByRole('button', { name: '11Q' })).toBeVisible({ timeout: TIMEOUT });
    });

    test('does not see login prompt', async ({ page }) => {
      await page.goto(KNOWN_VOTE_DETAIL_URL);
      await expect(page.getByText('投票するにはログインが必要です')).not.toBeVisible();
    });
  });
});

// ---------------------------------------------------------------------------
// Grade management page (/tasks/grade) — admin only
// ---------------------------------------------------------------------------

test.describe('grade management page (/tasks/grade)', () => {
  test('unauthenticated user is redirected to /login', async ({ page }) => {
    await page.goto(TASKS_GRADE_URL);
    await expect(page).toHaveURL(/\/login/, { timeout: TIMEOUT });
  });

  test('non-admin user is redirected to /', async ({ page }) => {
    await loginAsUser(page);
    await page.goto(TASKS_GRADE_URL);
    await expect(page).toHaveURL('/', { timeout: TIMEOUT });
  });

  test.describe('admin user', () => {
    test.beforeEach(async ({ page }) => {
      await loginAsAdmin(page);
    });

    test('can access the page', async ({ page }) => {
      await page.goto(TASKS_GRADE_URL);
      await expect(page).toHaveURL(TASKS_GRADE_URL, { timeout: TIMEOUT });
      await expect(page.getByRole('heading', { name: 'グレード管理' })).toBeVisible({
        timeout: TIMEOUT,
      });
    });

    test('shows search input and table with expected columns', async ({ page }) => {
      await page.goto(TASKS_GRADE_URL);
      await expect(page.getByPlaceholder('問題名・問題ID・出典で検索')).toBeVisible({
        timeout: TIMEOUT,
      });
      await expect(page.getByRole('columnheader', { name: '問題名' })).toBeVisible({
        timeout: TIMEOUT,
      });
      await expect(page.getByRole('columnheader', { name: 'グレード（admin）' })).toBeVisible({
        timeout: TIMEOUT,
      });
      await expect(page.getByRole('columnheader', { name: 'グレード（ユーザ投票）' })).toBeVisible({
        timeout: TIMEOUT,
      });
    });
  });
});
