import { test, expect, type Page } from '../../fixtures.js';
import { convexRun } from '../../helpers/convex.js';
import {
  createStudentPage,
  newApplicationId,
  openSubjectGroupPage,
} from '../../helpers/students.js';
import { unique } from '../../helpers/settings.js';

/** The last-page submit button ('students.application.submitData'). */
const submitButton = (page: Page) =>
  page.locator('p-button').filter({ hasText: /send.*application|bewerbung.*absenden/i }).first();

/**
 * Walk from the first subject-group page to the last one. Every group of a
 * fresh application is empty, so each "Next" click first pops the
 * "group is empty — continue anyway?" confirm dialog, which must be accepted
 * before the router navigates (the old skip-happy loops never accepted it,
 * so they never reached the last page). Every step is hard-asserted: a page
 * without next/submit, a missing confirm, or a navigation that never happens
 * all FAIL the test.
 */
async function walkToLastSubjectGroupPage(page: Page): Promise<void> {
  const nextButton = page.locator('p-button').filter({ hasText: /next|weiter/i }).first();
  const submit = submitButton(page);

  // Every student process has well under 12 subject groups.
  for (let i = 0; i < 12; i += 1) {
    await expect(
      submit.or(nextButton).first(),
      'subject-group page must offer a next or submit button',
    ).toBeVisible({ timeout: 15_000 });
    // Bounded probe, NOT an instant isVisible(): right after the router
    // navigates onto the last page, the or() above can resolve against
    // the not-yet-torn-down previous footer; an instant check then misses
    // the submit button and the loop tries to click a "Next" that never
    // exists on the last page. The probe gives the footer time to settle;
    // the click below carries its own timeout so nothing waits unbounded.
    const submitVisible = await submit
      .waitFor({ state: 'visible', timeout: 3_000 })
      .then(() => true)
      .catch(() => false);
    if (submitVisible) return;

    const urlBefore = page.url();
    await nextButton.click({ timeout: 15_000 });

    // Empty group => confirm dialog before navigation; accept it.
    const confirm = page.locator('[role="alertdialog"]:visible').first();
    await expect(
      confirm,
      'empty subject group must warn before navigating on',
    ).toBeVisible({ timeout: 10_000 });
    await confirm
      .locator('.p-confirmdialog-accept-button')
      .first()
      .click({ timeout: 10_000 });

    await expect(async () => {
      expect(page.url()).not.toBe(urlBefore);
    }).toPass({ timeout: 15_000 });
  }

  await expect(
    submit,
    'submit button must appear on the last subject-group page',
  ).toBeVisible({ timeout: 5_000 });
}

test.describe('Students: Submission', () => {
  test.beforeEach(({ features }) => {
    test.skip(!features.students, 'Students feature not enabled');
  });

  test('submit button appears on last subject group page', async ({
    browser,
    customerConfig,
    customerName,
  }) => {
    // Walking all subject groups (7 for AS) means one confirm dialog +
    // router navigation per group; with magic-link login on top the
    // per-step settle budgets legitimately stack far past the default
    // 120s when the whole suite runs in parallel (verified: 17s solo).
    test.slow();
    let context: { close: () => Promise<void> } | undefined;
    try {
      const opened = await openSubjectGroupPage(browser, customerConfig, customerName);
      context = opened.context;
      const page = opened.page;

      await walkToLastSubjectGroupPage(page);

      // The last subject-group page must offer the submit action.
      await expect(submitButton(page)).toBeVisible({ timeout: 5_000 });
    } finally {
      await context?.close();
    }
  });

  test('submit with empty subject groups shows warning', async ({
    browser,
    customerConfig,
    customerName,
  }) => {
    // Same full-walk budget as the submit-button test above.
    test.slow();
    let context: { close: () => Promise<void> } | undefined;
    try {
      const opened = await openSubjectGroupPage(browser, customerConfig, customerName);
      context = opened.context;
      const page = opened.page;

      await walkToLastSubjectGroupPage(page);
      await submitButton(page).click();

      // All subject groups of the fresh application are empty, so submitting
      // MUST warn ('emptyGroupsWarning' confirm with continue/stay choices).
      const confirm = page.locator('[role="alertdialog"]:visible').first();
      await expect(
        confirm,
        'submitting with empty subject groups must show the warning dialog',
      ).toBeVisible({ timeout: 10_000 });
      await expect(confirm.locator('.p-confirmdialog-accept-button').first()).toBeVisible();

      // Stay on the page (reject) — the warning itself is what this test is about.
      await confirm.locator('.p-confirmdialog-reject-button').first().click();
      await expect(confirm).not.toBeVisible({ timeout: 5_000 });
    } finally {
      await context?.close();
    }
  });

  test('download confirmation button exists for submitted applications', async ({
    browser,
    customerConfig,
    customerName,
  }) => {
    // A fresh magic-link student never has a submitted application and the UI
    // cannot submit an incomplete one, so seed the submitted state directly
    // instead of fishing for ambient staging data (the old variant fixme'd
    // itself away when none existed).
    if (!customerConfig.firstProcess) {
      throw new Error(
        `Customer ${customerName} has students enabled but no firstProcess configured — cannot seed a student application.`,
      );
    }
    const email = `e2e-submitted-${unique()}@e2e.local`;
    const studentId = await convexRun<string>('e2e:createStudent', {
      customer: customerName,
      email,
      firstName: 'E2E',
      lastName: 'Submitted',
    });
    await convexRun('e2e:createStudentApplication', {
      customer: customerName,
      studentId,
      process: customerConfig.firstProcess,
      applicationId: newApplicationId(),
      periodShort: 'E2E',
      submitted: true,
    });

    let context: { close: () => Promise<void> } | undefined;
    try {
      const result = await createStudentPage(
        browser,
        customerConfig,
        customerName,
        'e2e-submitted',
        { email },
      );
      context = result.context;
      const page = result.page;

      await page.goto(`${customerConfig.url}/students/application`);
      await expect(page).toHaveURL(/\/students/, { timeout: 15_000 });

      // Submitted application => the download confirmation button must render.
      const downloadButton = page.locator('p-button').filter({
        hasText: /download.*confirm|bestätigung.*herunterladen/i,
      }).first();
      await expect(
        downloadButton,
        'download button must render for a submitted application',
      ).toBeVisible({ timeout: 15_000 });
    } finally {
      await context?.close();
    }
  });
});
