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

/**
 * User Journey E2E Tests
 *
 * Simulates a complete user flow: register → login → browse → search → settings → logout.
 * All tests are serial because they share browser state (auth token, navigation).
 *
 * FORBIDDEN: Do NOT use page.waitForTimeout() — use waitForResponse() or waitForSelector()
 */

test.describe('User Journey E2E', () => {
  test.describe.configure({ mode: 'serial' });

  const testData = {
    username: `e2e_user_${Date.now()}`,
    password: 'TestPass123!',
  };

  // Register a new user account via the UI form
  test('should register a new user', async ({ page }) => {
    await page.goto('/login');

    // Switch to the register tab
    await page.click('text=Inscription');

    // Fill out the registration form
    await page.fill('#registerUsername', testData.username);
    await page.fill('#registerPassword', testData.password);
    await page.fill('#registerPasswordConfirm', testData.password);

    // Submit and wait for the API response
    const [response] = await Promise.all([
      page.waitForResponse((resp) => resp.url().includes('/api/auth/register')),
      page.click('#registerSubmit'),
    ]);

    // Registration should succeed (201 or 200)
    expect(response.status()).toBeLessThan(400);

    // Verify the success message appears
    await page.locator('#authSuccess').waitFor({ state: 'visible', timeout: 10000 });
    const successText = await page.locator('#authSuccess').textContent();
    expect(successText).toMatch(/réussie|inscription/i);
  });

  // Login with the credentials registered in the previous test
  test('should login with registered credentials', async ({ page }) => {
    await page.goto('/login');

    await page.fill('#loginUsername', testData.username);
    await page.fill('#loginPassword', testData.password);

    // Submit and wait for the login API response
    const [response] = await Promise.all([
      page.waitForResponse((resp) => resp.url().includes('/api/auth/login')),
      page.click('#loginSubmit'),
    ]);

    expect(response.status()).toBeLessThan(400);

    // Verify success message
    await page.locator('#authSuccess').waitFor({ state: 'visible', timeout: 10000 });
    const successText = await page.locator('#authSuccess').textContent();
    expect(successText).toMatch(/réussie/i);

    // Wait for redirect to /web
    await page.waitForURL('**/web**', { timeout: 10000 });

    // Verify the auth token is stored in localStorage
    const token = await page.evaluate(() => localStorage.getItem('auth_token'));
    expect(token).toBeTruthy();
  });

  // Browse the homepage — verify layout loads without JS errors
  test('should browse homepage without errors', async ({ page }) => {
    // Collect JS page errors
    const errors: string[] = [];
    page.on('pageerror', (err) => errors.push(err.message));

    // Ensure we are on /web (carried over from login)
    if (!page.url().includes('/web')) {
      await page.goto('/web');
    }

    // Wait for main content area to be visible
    await page.locator('#main-content').waitFor({ state: 'visible', timeout: 10000 });

    // Verify the header heading
    await expect(page.locator('header h1')).toContainText('Ohm Stream');

    // Verify at least one navigation tab is visible
    await expect(page.locator('.tab').first()).toBeVisible();

    // Verify the user info panel (logged-in state indicator)
    await expect(page.locator('#userInfo')).toBeVisible();

    // No JavaScript errors should have been thrown
    expect(errors).toHaveLength(0);
  });

  // Search for an anime via the Anime tab — results may be empty but the UI must respond
  test('should search for anime', async ({ page }) => {
    // Navigate to the Anime tab
    await page.click('.tab:has-text("Anime")');
    await page.locator('#tab-anime').waitFor({ state: 'visible', timeout: 5000 });

    // Fill the search input — HTMX debounce triggers the request automatically
    await page.fill('#animeSearchInput', 'Naruto');

    // Wait for either results, an empty-state message, or the loading spinner to disappear
    await Promise.race([
      page.locator('#animeSearchResults .sr-card').first().waitFor({ timeout: 15000 }),
      page.locator('#animeSearchResults .sr-empty').first().waitFor({ timeout: 15000 }),
      page.locator('#search-loading').waitFor({ state: 'detached', timeout: 15000 }),
    ]);

    // The search results container must be present regardless of result count
    await expect(page.locator('#animeSearchResults')).toBeVisible();
  });

  // Change a setting (language) and verify the PATCH response and toast notification
  test('should update settings', async ({ page }) => {
    // Open the settings tab
    await page.click('.tab:has-text("Paramètres")');

    // Settings panel is loaded dynamically via HTMX — wait for the form
    await page.locator('#default_lang').waitFor({ state: 'visible', timeout: 15000 });

    // Change the default language
    await page.selectOption('#default_lang', 'vf');

    // Submit the settings form and capture the PATCH response
    const [response] = await Promise.all([
      page.waitForResponse(
        (resp) => resp.url().includes('/api/settings') && resp.request().method() === 'PATCH'
      ),
      page.locator('form[hx-patch="/api/settings"] button[type="submit"]').click(),
    ]);

    expect(response.status()).toBe(200);

    // Verify a toast notification appears confirming the save
    await page.locator('.toast').first().waitFor({ state: 'visible', timeout: 5000 });
  });

  // Logout — verify the API call succeeds, redirect happens, and token is cleared
  test('should logout successfully', async ({ page }) => {
    // Click the logout button and wait for the API response
    const [response] = await Promise.all([
      page.waitForResponse((resp) => resp.url().includes('/api/auth/logout')),
      page.locator('#userInfo button:has-text("Déconnexion")').click(),
    ]);

    expect(response.status()).toBeLessThan(400);

    // Should be redirected back to the login page
    await page.waitForURL('**/login**', { timeout: 10000 });

    // The auth token must be cleared from localStorage
    const token = await page.evaluate(() => localStorage.getItem('auth_token'));
    expect(token).toBeNull();
  });
});
