import { test, expect, type Page } from './fixtures';

// Playwright's WebKit has no real soft keyboard, so emulate iOS's visual-viewport behaviour: replace
// window.visualViewport with a controllable fake BEFORE the app boots. The app's syncViewport
// (app.svelte.ts) reads visualViewport.height/offsetTop and mirrors them into --vvh / --vv-top, which
// position the pinned app-shell (app.css html.app-shell body). Driving the fake exercises the exact
// code path the real keyboard triggers on iOS — where the layout viewport does NOT shrink and the
// visual viewport instead offsets down toward the focused field.
async function installFakeViewport(page: Page): Promise<void> {
  await page.addInitScript(() => {
    const fake = new EventTarget() as EventTarget & { height: number; offsetTop: number; width: number };
    fake.height = window.innerHeight;
    fake.offsetTop = 0;
    fake.width = window.innerWidth;
    Object.defineProperty(window, 'visualViewport', { configurable: true, value: fake });
    (window as unknown as { __vv: typeof fake }).__vv = fake;
  });
}

async function setViewport(page: Page, height: number, offsetTop: number): Promise<void> {
  await page.evaluate(
    ([h, t]) => {
      const vv = (window as unknown as { __vv: { height: number; offsetTop: number; dispatchEvent(e: Event): boolean } }).__vv;
      vv.height = h;
      vv.offsetTop = t;
      vv.dispatchEvent(new Event('resize'));
      vv.dispatchEvent(new Event('scroll'));
    },
    [height, offsetTop],
  );
}

async function shell(page: Page): Promise<{ vvh: string; top: string; bodyTop: string }> {
  return page.evaluate(() => ({
    vvh: getComputedStyle(document.documentElement).getPropertyValue('--vvh').trim(),
    top: getComputedStyle(document.documentElement).getPropertyValue('--vv-top').trim(),
    bodyTop: getComputedStyle(document.body).top,
  }));
}

test.describe('visual-viewport shell (soft-keyboard alignment)', () => {
  test('the pinned shell follows the visual viewport height AND offset', async ({ page }) => {
    await installFakeViewport(page);
    await page.goto('/');
    await expect(page.locator('html.app-shell')).toBeAttached();
    const innerH = await page.evaluate(() => window.innerHeight);

    // Keyboard closed: full height, no offset.
    await setViewport(page, innerH, 0);
    let s = await shell(page);
    expect(s.top).toBe('0px');
    expect(s.bodyTop).toBe('0px');

    // Keyboard open + iOS offset: the visual viewport shrinks AND offsets down. The pinned shell must
    // follow BOTH — the body's top must equal the offset (not stay at 0), else the top-anchored
    // content shows empty space below (the iOS bug this fixes).
    await setViewport(page, innerH - 300, 180);
    s = await shell(page);
    expect(s.vvh).toBe(`${innerH - 300}px`);
    expect(s.top).toBe('180px');
    expect(s.bodyTop).toBe('180px');

    // Keyboard closed again: the offset reverts to 0 — no stale shift left behind.
    await setViewport(page, innerH, 0);
    s = await shell(page);
    expect(s.top).toBe('0px');
    expect(s.bodyTop).toBe('0px');
  });
});
