import { test, expect, UI_SETTLE_TIMEOUT } from '../../fixtures.js';
import {
  unique,
  saveDialog,
  searchAndFind,
  openRowEditor,
  deleteViaDialog,
  expectGoneFromSearch,
} from '../../helpers/settings.js';
import { createUniversityViaUI } from '../../helpers/universities.js';
import { convexRun } from '../../helpers/convex.js';

test.describe('Settings: Universities', () => {
  test.beforeEach(async ({ features, customerConfig }) => {
    test.skip(!features.universities, 'Universities feature not enabled');
    // Ensure the real country reference data exists before the form's
    // required country dropdown is used: staging accumulates test-created
    // countries that are never swept, and once the real ones were deleted
    // the dropdown matches nothing and the save button stays disabled.
    // Seed the precondition; never skip over it.
    await convexRun('e2e:ensureCountries', { customer: customerConfig.name });
  });

  test('universities page loads and shows list', async ({ adminPage }) => {
    await adminPage.goto('/admin/settings/universities');
    await expect(adminPage).toHaveURL(/\/settings\/universities/);
    await expect(adminPage.locator('masterev-infinite-data-table').first()).toBeVisible({ timeout: 10_000 });
  });

  test('create a new university via dialog', async ({ adminPage }) => {
    await adminPage.goto('/admin/settings/universities');
    await createUniversityViaUI(adminPage, `E2E University ${unique()}`);
  });

  test('edit an existing university via dialog', async ({ adminPage }) => {
    const uniName = `E2E Row ${unique()}`;
    await adminPage.goto('/admin/settings/universities');

    // Seed the row we edit through the UI itself — "no data rows" is a
    // seeding task, not a skip.
    await createUniversityViaUI(adminPage, uniName);

    // openRowEditor scopes the edit click to the matching row and
    // asserts the dialog hydrated with the university's name.
    const dialog = await openRowEditor(adminPage, uniName);

    // Close without saving
    await dialog.locator('[data-testid="cancel-button"]').first().click();
    await expect(dialog).not.toBeVisible({ timeout: 5_000 });
  });

  test('create university and verify via search', async ({ adminPage }) => {
    await adminPage.goto('/admin/settings/universities');

    const uniName = `E2E Uni ${unique()}`;
    await createUniversityViaUI(adminPage, uniName);

    // Verify via search; a row that never appears means the create failed.
    await searchAndFind(adminPage, uniName);
  });

  test('create and fully edit a university', async ({ adminPage }) => {
    const id = unique();
    const uniName = `E2E Edit ${id}`;
    const editedName = `E2E Edited ${id}`;
    await adminPage.goto('/admin/settings/universities');

    await createUniversityViaUI(adminPage, uniName);

    // Open the created university and edit the name
    const dialog = await openRowEditor(adminPage, uniName);
    await dialog.locator('input').first().fill(editedName);
    await saveDialog(dialog);

    // Verify the edited name via search
    await searchAndFind(adminPage, editedName);
  });

  test('default factor value persists after save', async ({ adminPage }) => {
    const id = unique();
    const uniName = `E2E Factor ${id}`;
    await adminPage.goto('/admin/settings/universities');

    await createUniversityViaUI(adminPage, uniName);

    // Re-open the university; openRowEditor asserts the dialog hydrated
    // with exactly this university (kills the wrong-entity race).
    const dialog = await openRowEditor(adminPage, uniName);

    // Factor should be 1.00 (default), verify it persisted with decimal format
    const factorInput = dialog.locator('p-inputnumber input').first();
    await expect(factorInput).toBeVisible({ timeout: 3_000 });
    // The component has minFractionDigits=2, so "1" displays as "1.00" or "1,00".
    // p-inputNumber formats via writeValue on a later CD tick after the dialog
    // fetches the university, so give the read-back the full settle budget.
    await expect(factorInput).toHaveValue(/^1[.,]00$/, {
      timeout: UI_SETTLE_TIMEOUT,
    });

    await dialog.locator('[data-testid="cancel-button"]').first().click();
    await expect(dialog).not.toBeVisible({ timeout: 5_000 });
  });

  test('comment persistence', async ({ adminPage }) => {
    const id = unique();
    const uniName = `E2E Comment ${id}`;
    const comment = `Test comment ${id}`;
    await adminPage.goto('/admin/settings/universities');

    await createUniversityViaUI(adminPage, uniName);

    // Open and add a comment
    const dialog = await openRowEditor(adminPage, uniName);
    const commentTextarea = dialog.locator('textarea').first();
    await expect(commentTextarea).toBeVisible({ timeout: 3_000 });
    await commentTextarea.fill(comment);
    await saveDialog(dialog);

    // Re-open and verify the comment was saved
    const editDialog = await openRowEditor(adminPage, uniName);
    const savedComment = editDialog.locator('textarea').first();
    await expect(savedComment).toHaveValue(comment, { timeout: 3_000 });

    await editDialog.locator('[data-testid="cancel-button"]').first().click();
    await expect(editDialog).not.toBeVisible({ timeout: 5_000 });
  });

  test('toggle isActive and verify persistence', async ({ adminPage }) => {
    const id = unique();
    const uniName = `E2E Active ${id}`;
    await adminPage.goto('/admin/settings/universities');

    await createUniversityViaUI(adminPage, uniName);

    // Open and toggle isActive
    const dialog = await openRowEditor(adminPage, uniName);

    const toggleHost = dialog.locator('p-toggleswitch').first();
    await expect(toggleHost).toBeVisible({ timeout: 3_000 });
    const toggle = dialog.getByRole('switch').first();
    const wasChecked = await toggle.isChecked();

    // PrimeNG v21 ToggleSwitch: the click handler is a HOST listener on <p-toggleswitch>.
    // Use Playwright's real click (not evaluate) on the host element to trigger Angular's handler.
    await toggleHost.click();

    // Verify the toggle state changed
    await expect(async () => {
      const isNowChecked = await toggle.isChecked();
      expect(isNowChecked).not.toBe(wasChecked);
    }).toPass({ timeout: 3_000 });

    await saveDialog(dialog);

    // Re-open and verify the toggle state was persisted.
    // The admin list does NOT filter by isActive, so the university is
    // always visible. Reload the page to get a fresh view of the data.
    await adminPage.goto('/admin/settings/universities');
    const editDialog = await openRowEditor(adminPage, uniName);

    const savedToggle = editDialog.getByRole('switch').first();
    await expect(savedToggle).toBeVisible({ timeout: 3_000 });
    if (wasChecked) {
      await expect(savedToggle).not.toBeChecked();
    } else {
      await expect(savedToggle).toBeChecked();
    }

    await editDialog.locator('[data-testid="cancel-button"]').first().click();
    await expect(editDialog).not.toBeVisible({ timeout: 5_000 });
  });

  test('history button opens entity history panel', async ({ adminPage }) => {
    const id = unique();
    const uniName = `E2E History ${id}`;
    await adminPage.goto('/admin/settings/universities');

    await createUniversityViaUI(adminPage, uniName);

    const dialog = await openRowEditor(adminPage, uniName);

    const historyButton = dialog.locator('[data-testid="history-button"]').first();
    await expect(historyButton).toBeVisible({ timeout: 3_000 });
    await historyButton.click();

    // Verify history drawer opens (PrimeNG Drawer renders with role="complementary")
    const drawer = adminPage.locator('[role="complementary"]').first();
    await expect(drawer).toBeVisible({ timeout: 10_000 });

    await adminPage.keyboard.press('Escape');
    await expect(drawer).not.toBeVisible({ timeout: 5_000 });

    await dialog.locator('[data-testid="cancel-button"]').first().click();
    await expect(dialog).not.toBeVisible({ timeout: 10_000 });
  });

  test('custom factor value persists after save', async ({ adminPage }) => {
    const id = unique();
    const uniName = `E2E Factor ${id}`;
    await adminPage.goto('/admin/settings/universities');

    await createUniversityViaUI(adminPage, uniName, '1.25');

    // Re-open the university; openRowEditor asserts the dialog hydrated
    // with exactly this university (kills the wrong-entity race).
    const dialog = await openRowEditor(adminPage, uniName);

    // Factor should be 1.25, verify it persisted with correct decimal format
    const factorInput = dialog.locator('p-inputnumber input').first();
    await expect(factorInput).toBeVisible({ timeout: 3_000 });
    // Use a fractional value to properly test decimal parsing and persistence
    await expect(factorInput).toHaveValue(/^1[.,]25$/, { timeout: 3_000 });

    await dialog.locator('[data-testid="cancel-button"]').first().click();
    await expect(dialog).not.toBeVisible({ timeout: 5_000 });
  });

  test('existing university edit dialog offers delete', async ({ adminPage }) => {
    // The former frontend isDeletable guard ("referenced universities hide
    // the delete button") no longer exists: university-edit sets
    // isDeletable for EVERY existing university and the backend cascade-
    // deletes referencing courses on removal (universities trigger). The
    // current contract is therefore: an existing university's edit dialog
    // always offers the delete button.
    const uniName = `E2E Deletable ${unique()}`;
    await adminPage.goto('/admin/settings/universities');

    await createUniversityViaUI(adminPage, uniName);

    const dialog = await openRowEditor(adminPage, uniName);
    await expect(dialog.locator('[data-testid="delete-button"]').first()).toBeVisible({
      timeout: 5_000,
    });

    await dialog.locator('[data-testid="cancel-button"]').first().click();
    await expect(dialog).not.toBeVisible({ timeout: 5_000 });
  });

  test('create and delete a university', async ({ adminPage }) => {
    await adminPage.goto('/admin/settings/universities');

    const uniName = `E2E Uni ${unique()}`;
    await createUniversityViaUI(adminPage, uniName);

    // Open the created university and delete it (delete + confirm +
    // hard close assert live in deleteViaDialog).
    const dialog = await openRowEditor(adminPage, uniName);
    await deleteViaDialog(adminPage, dialog);

    // Reload and verify the university is no longer in the list
    await expectGoneFromSearch(adminPage, uniName);
  });
});
