import { test, expect } from '../../fixtures/multi-client';
import { LoginPage } from '../../pages/login.page';
import { RegisterPage } from '../../pages/register.page';

interface TestUser {
  username: string;
  displayName: string;
  password: string;
}

test.describe('Login returnUrl handling', () => {
  test.describe.configure({ timeout: 120_000 });

  test('unwraps nested login returnUrl chains after successful login', async ({ createClient }) => {
    const client = await createClient();
    const { page } = client;
    const suffix = uniqueName('nested-return');
    const user: TestUser = {
      username: `user_${suffix}`,
      displayName: 'Return Url User',
      password: 'TestPass123!'
    };

    await test.step('Create an account', async () => {
      const registerPage = new RegisterPage(page);

      await registerPage.goto();
      await registerPage.register(user.username, user.displayName, user.password);
      await expect(page).toHaveURL(/\/dashboard/, { timeout: 15_000 });
    });

    await test.step('Log out and open a deeply nested login returnUrl', async () => {
      await logout(page);

      const nestedReturnUrl = '/login?returnUrl=%2Flogin%3FreturnUrl%3D%252Fservers';

      await page.goto(`/login?returnUrl=${encodeURIComponent(nestedReturnUrl)}`, {
        waitUntil: 'domcontentloaded'
      });

      await expect(page.locator('#login-username')).toBeVisible({ timeout: 15_000 });
    });

    await test.step('Login lands on the original destination instead of looping on /login', async () => {
      const loginPage = new LoginPage(page);

      await loginPage.login(user.username, user.password);
      await expect(page).toHaveURL(/\/servers/, { timeout: 15_000 });
      await expect(page).not.toHaveURL(/returnUrl=.*login/);
    });
  });

  test('redirects unauthenticated /servers visits to login and returns there after login', async ({ createClient }) => {
    const client = await createClient();
    const { page } = client;
    const suffix = uniqueName('servers-return');
    const user: TestUser = {
      username: `user_${suffix}`,
      displayName: 'Servers Return User',
      password: 'TestPass123!'
    };

    await test.step('Create an account and log out', async () => {
      const registerPage = new RegisterPage(page);

      await registerPage.goto();
      await registerPage.register(user.username, user.displayName, user.password);
      await expect(page).toHaveURL(/\/dashboard/, { timeout: 15_000 });
      await logout(page);
    });

    await test.step('Visiting /servers sends the user to a single-level login returnUrl', async () => {
      await page.goto('/servers', { waitUntil: 'domcontentloaded' });
      await expect(page).toHaveURL(/\/login/, { timeout: 15_000 });
      await expect(page).toHaveURL(/returnUrl=%2Fservers/);
      await expect(page).not.toHaveURL(/returnUrl=.*login/);
    });

    await test.step('Logging in returns to /servers', async () => {
      const loginPage = new LoginPage(page);

      await loginPage.login(user.username, user.password);
      await expect(page).toHaveURL(/\/servers/, { timeout: 15_000 });
      await expect(page.locator('app-server-browser')).toBeVisible({ timeout: 15_000 });
    });
  });

  test('lets a returning user log back in after an expired session redirect', async ({ createClient }) => {
    const client = await createClient();
    const { page } = client;
    const suffix = uniqueName('expired-session');
    const user: TestUser = {
      username: `user_${suffix}`,
      displayName: 'Expired Session User',
      password: 'TestPass123!'
    };

    await test.step('Create an account', async () => {
      const registerPage = new RegisterPage(page);

      await registerPage.goto();
      await registerPage.register(user.username, user.displayName, user.password);
      await expect(page).toHaveURL(/\/dashboard/, { timeout: 15_000 });
    });

    await test.step('Simulate an expired session while keeping the persisted user id', async () => {
      await page.evaluate(() => {
        const storageKey = 'metoyou.authTokens';
        const raw = localStorage.getItem(storageKey);

        if (!raw) {
          return;
        }

        const parsed = JSON.parse(raw) as Record<string, { token: string; expiresAt: number }>;
        const expiredStore = Object.fromEntries(
          Object.entries(parsed).map(([url, entry]) => [url, { ...entry, expiresAt: 0 }])
        );

        localStorage.setItem(storageKey, JSON.stringify(expiredStore));
      });

      await page.goto('/servers', { waitUntil: 'domcontentloaded' });
      await expect(page).toHaveURL(/\/login/, { timeout: 15_000 });
      await expect(page).toHaveURL(/returnUrl=%2Fservers/);
      await expect(page).not.toHaveURL(/returnUrl=.*login/);
    });

    await test.step('The user can authenticate again and reach /servers', async () => {
      const loginPage = new LoginPage(page);

      await loginPage.login(user.username, user.password);
      await expect(page).toHaveURL(/\/servers/, { timeout: 15_000 });
      await expect(page.locator('app-server-browser')).toBeVisible({ timeout: 15_000 });
    });
  });
});

async function logout(page: import('@playwright/test').Page): Promise<void> {
  const menuButton = page.getByRole('button', { name: 'Menu' });
  const logoutButton = page.getByRole('button', { name: 'Logout' });

  await expect(menuButton).toBeVisible({ timeout: 10_000 });
  await menuButton.click();
  await expect(logoutButton).toBeVisible({ timeout: 10_000 });
  await logoutButton.click();
  await expect(page).toHaveURL(/\/login/, { timeout: 15_000 });
}

function uniqueName(prefix: string): string {
  return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36)
    .slice(2, 8)}`;
}
