import { test, expect, type Page, type APIRequestContext } from '@playwright/test';
import fs from 'node:fs';
import path from 'node:path';
import { execFileSync } from 'node:child_process';

export { test, expect };
export const runDir = process.env.QA_RUN_DIR!;
export const fixture = JSON.parse(fs.readFileSync(path.join(runDir, 'manifest.json'), 'utf8'));
if (fixture.marker !== process.env.QA_MARKER || fixture.database !== path.join(runDir, 'database.sqlite')) throw new Error('Fixture identity mismatch');
export type Actor = 'owner' | 'manager' | 'staff' | 'outsider';
export const API = 'http://127.0.0.1:8105';
export const storePath = (suffix: string, store: 'a' | 'b' = 'a') => `/v1/stores/${fixture.stores[store].id}${suffix}`;

export async function api(request: APIRequestContext, actor: Actor, method: string, suffix: string, data?: unknown, status = 200, store: 'a' | 'b' = 'a', key?: string) {
  const response = await request.fetch(API + storePath(suffix, store), {
    method, data, headers: { Authorization: `Bearer ${fixture.actors[actor].token}`, Accept: 'application/json', ...(key ? { 'Idempotency-Key': key } : {}) },
  });
  // Never include request options/credentials in failure messages.
  const text = await response.text();
  expect(response.status(), `${method} ${suffix}: ${text.slice(0, 1800)}`).toBe(status);
  return text ? JSON.parse(text).data : undefined;
}

export async function login(page: Page, actor: Actor, target: string, store: 'a' | 'b' = 'a') {
  await page.goto('/login');
  await page.getByLabel('Email', { exact: true }).fill(fixture.actors[actor].email);
  await page.getByLabel('Password', { exact: true }).fill(fixture.actors[actor].password);
  await page.getByRole('button', { name: 'Sign In', exact: true }).click();
  await expect.poll(async () => page.evaluate(() => !!localStorage.getItem('auth_token'))).toBe(true);
  // setAuth stores the token before membership discovery and the login redirect.
  // Let that real redirect settle; navigating on token presence races router.push.
  await page.waitForURL(url => url.pathname === '/');
  await page.goto(target);
  const selector = page.getByRole('combobox', { name: 'Store', exact: true });
  await expect(selector).toBeVisible();
  await selector.selectOption(fixture.stores[store].id);
}

export function dbRows(table: 'shifts' | 'time_punches' | 'timesheets' | 'payroll_exports', store: 'a' | 'b' = 'a') {
  return JSON.parse(execFileSync('python3', [path.join(__dirname, 'readback.py'), runDir, table, fixture.stores[store].id], { encoding: 'utf8' }));
}

export async function evidence(name: string, value: unknown) {
  const body = JSON.stringify(value, null, 2);
  fs.writeFileSync(path.join(runDir, `${name}.json`), body, { mode: 0o600 });
  await test.info().attach(name, { body, contentType: 'application/json' });
}
