/**
 * Scroll-Wiederherstellung (useScrollRestoration im AppShell).
 *
 * Regression: Liste runterscrollen → Eintrag öffnen → Browser-Zurück → die Position
 * muss erhalten bleiben (vorher sprang sie auf 0, weil beim Wegnavigieren der kollabierende
 * Inhalt ein scroll→0 auslöste, das die gemerkte Position überschrieb).
 *
 * Gescrollt wird der Container `.app-main` (nicht das Fenster). Läuft in beiden Projekten
 * (desktop + phone) — die Rennmausliste ist in beiden Viewports hoch genug zum Scrollen.
 */
import { test, expect, skipUnlessMock, de } from './fixtures'

const SCROLLER = '.app-main'

async function scrollerTop(page: import('@playwright/test').Page) {
  return page.evaluate((sel) => document.querySelector(sel)?.scrollTop ?? 0, SCROLLER)
}

/**
 * Realistisch runterscrollen: ein Warm-up-Schritt (synchronisiert die gemerkte Höhe nach
 * dem ersten Render) + der eigentliche Schritt — wie echtes Scrollen, das viele Events feuert.
 */
async function scrollDown(page: import('@playwright/test').Page, target: number) {
  await page.evaluate((sel) => {
    const el = document.querySelector(sel)
    if (el) el.scrollTop = 60
  }, SCROLLER)
  await page.waitForTimeout(80)
  await page.evaluate(
    ([sel, t]) => {
      const el = document.querySelector(sel as string)
      if (el) el.scrollTop = Math.min(t as number, el.scrollHeight - el.clientHeight)
    },
    [SCROLLER, target] as const,
  )
  await page.waitForTimeout(150) // rAF-gedrosseltes Speichern abwarten
}

test.describe('Scroll-Wiederherstellung', () => {
  test.beforeEach(() => skipUnlessMock())

  test('Rennmausliste: Position überlebt Detail-Öffnen + Browser-Zurück', async ({ page }) => {
    await page.goto('/rennmaeuse')
    // Warten bis Tierkarten-Links da sind.
    await expect(page.locator('a[href^="/rennmaeuse/"]').first()).toBeVisible()

    // Container muss scrollbar sein, sonst ist der Test sinnlos.
    const scrollable = await page.evaluate((sel) => {
      const el = document.querySelector(sel)
      return el ? el.scrollHeight > el.clientHeight + 50 : false
    }, SCROLLER)
    expect(scrollable, 'Liste muss scrollbar sein').toBeTruthy()

    // Ein Stück runterscrollen.
    await scrollDown(page, 400)
    const before = await scrollerTop(page)
    expect(before).toBeGreaterThan(50)

    // Einen AKTUELL SICHTBAREN Tier-Link öffnen (kein Auto-Scroll, wie ein echter Klick).
    const href = await page.evaluate(() => {
      for (const a of document.querySelectorAll('a[href^="/rennmaeuse/"]')) {
        const r = a.getBoundingClientRect()
        if (r.top >= 0 && r.bottom <= window.innerHeight && a.getAttribute('href') !== '/rennmaeuse/neu') {
          ;(a as HTMLElement).click()
          return a.getAttribute('href')
        }
      }
      return null
    })
    expect(href).toBeTruthy()
    await expect(page).toHaveURL(/\/rennmaeuse\/[^/]+$/)

    // Zurück — Position muss (nahezu) wiederhergestellt sein.
    await page.goBack()
    await expect(page.locator('a[href^="/rennmaeuse/"]').first()).toBeVisible()
    await expect
      .poll(async () => scrollerTop(page), { timeout: 4000, message: 'Scrollposition wiederhergestellt' })
      .toBeGreaterThan(before - 30)
  })

  test('Rennmausliste: Position überlebt App-Hintergrund (visibilitychange)', async ({ page }) => {
    await page.goto('/rennmaeuse')
    await expect(page.locator('a[href^="/rennmaeuse/"]').first()).toBeVisible()
    await scrollDown(page, 400)
    const before = await scrollerTop(page)
    test.skip(before < 50, 'Liste in diesem Viewport nicht hoch genug')

    // App in den Hintergrund (Handy-Sperre) und zurück simulieren.
    await page.evaluate(() => {
      Object.defineProperty(document, 'visibilityState', { configurable: true, get: () => 'hidden' })
      document.dispatchEvent(new Event('visibilitychange'))
    })
    // Inhalt „springt" (wie beim Aufwachen) künstlich nach oben …
    await page.evaluate((sel) => {
      const el = document.querySelector(sel)
      if (el) el.scrollTop = 0
    }, SCROLLER)
    await page.evaluate(() => {
      Object.defineProperty(document, 'visibilityState', { configurable: true, get: () => 'visible' })
      document.dispatchEvent(new Event('visibilitychange'))
    })

    // … und muss wiederhergestellt werden.
    await expect
      .poll(async () => scrollerTop(page), { timeout: 4000 })
      .toBeGreaterThan(before - 30)
  })

  test('Tickets: gewählter Tab überlebt Browser-Zurück (nicht zurück auf „Offen")', async ({ page }) => {
    const tt = de.feedback.tickets
    await page.goto('/hilfe/tickets')
    // In die „Geschlossen"-Ansicht wechseln.
    await page.locator('.tickets-filter').filter({ hasText: tt.filters.closed }).click()
    await expect(
      page.locator('.tickets-filter--active').filter({ hasText: tt.filters.closed }),
    ).toBeVisible()

    // Über die Brotkrümel weg und per Browser-Zurück wieder her.
    await page.locator('.tickets-breadcrumb a[href="/hilfe"]').click()
    await expect(page).toHaveURL(/\/hilfe$/)
    await page.goBack()

    // Tab muss weiterhin „Geschlossen" sein (vorher sprang er zurück auf „Offen").
    await expect(
      page.locator('.tickets-filter--active').filter({ hasText: tt.filters.closed }),
    ).toBeVisible()
  })
})
