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

export const PORT_SLUG = 'port-nimara';

export const USERS = {
  super_admin: {
    email: 'admin@portnimara.test',
    password: 'SuperAdmin12345!',
  },
  sales_agent: {
    email: 'agent@portnimara.test',
    password: 'SalesAgent12345!',
  },
  viewer: {
    email: 'viewer@portnimara.test',
    password: 'ViewerUser12345!',
  },
};

/**
 * Log in as a specific user via the UI login page.
 * Waits for the dashboard to load after successful login.
 */
export async function login(page: Page, role: keyof typeof USERS = 'super_admin') {
  const user = USERS[role];

  await page.goto('/login');
  // The email/username field id is `identifier` (accepts either).
  await page.waitForSelector('#identifier', { state: 'visible' });

  await page.fill('#identifier', user.email);
  await page.fill('#password', user.password);
  await page.click('button[type="submit"]');

  // Wait for redirect away from /login
  await page.waitForURL((url) => !url.pathname.includes('/login'), {
    timeout: 15_000,
  });
}

/**
 * Log out via the topbar user menu.
 * Falls back to navigating to /login if the logout button isn't found.
 */
export async function logout(page: Page) {
  // Try clicking a logout button/link if visible
  const logoutBtn = page.getByRole('button', { name: /log\s?out|sign\s?out/i });
  if (await logoutBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
    await logoutBtn.click();
    await page.waitForURL('**/login**', { timeout: 10_000 });
    return;
  }

  // Fallback: clear cookies and navigate to login
  await page.context().clearCookies();
  await page.goto('/login');
  await page.waitForSelector('#email', { state: 'visible' });
}

/**
 * Navigate to a page within the current port context.
 */
export async function navigateTo(page: Page, path: string) {
  const url = `/${PORT_SLUG}${path.startsWith('/') ? path : `/${path}`}`;
  await page.goto(url);
  await page.waitForLoadState('networkidle');
}

/**
 * Wait for a toast notification and verify its text.
 */
export async function expectToast(page: Page, textPattern: string | RegExp) {
  const toast = page.locator('[data-sonner-toast]').last();
  await expect(toast).toBeVisible({ timeout: 10_000 });
  if (typeof textPattern === 'string') {
    await expect(toast).toContainText(textPattern);
  } else {
    await expect(toast).toHaveText(textPattern);
  }
}

/**
 * Wait for a sheet (slide-in panel) to be visible.
 */
export async function waitForSheet(page: Page) {
  await page.waitForSelector('[role="dialog"]', {
    state: 'visible',
    timeout: 5_000,
  });
}

/**
 * Resolve the port-nimara port ID via API.
 *
 * Used by tests that drive the JSON API directly with `page.request.*`.
 * The server-side `withAuth` helper resolves port context from the
 * `X-Port-Id` header (or the user's default-port preference), so any
 * direct API call outside a port-scoped URL has to set the header. This
 * caches the lookup per page so the lookup happens once.
 */
const portIdCache = new WeakMap<Page, string>();
export async function getPortId(page: Page): Promise<string> {
  const cached = portIdCache.get(page);
  if (cached) return cached;
  const res = await page.request.get('/api/v1/admin/ports');
  if (!res.ok()) {
    throw new Error(`Failed to resolve port id: ${res.status()} ${await res.text()}`);
  }
  const body = (await res.json()) as { data?: Array<{ id: string; slug: string }> };
  const port = body.data?.find((p) => p.slug === PORT_SLUG);
  if (!port) {
    throw new Error(`Port ${PORT_SLUG} not in admin ports response`);
  }
  portIdCache.set(page, port.id);
  return port.id;
}

/**
 * Build headers for direct JSON-API calls inside tests, including the
 * `X-Port-Id` that the auth helper requires.
 */
export async function apiHeaders(page: Page): Promise<Record<string, string>> {
  return {
    'Content-Type': 'application/json',
    'X-Port-Id': await getPortId(page),
  };
}
