import { Page } from '@playwright/test'

/**
 * Waits for a map instance to become idle (not moving or zooming).
 *
 * This function first attempts to wait for a test marker element with data-testid="map-idle"
 * for up to 1 second, then polls the global map instance to check if it's in an idle state.
 * The function will timeout after 4 seconds if the map doesn't become idle.
 *
 */
export async function waitForMapIdle(page: Page): Promise<void> {
  const timeout = 4000
  const start = Date.now()

  try {
    await page.getByTestId('map-idle').waitFor({ timeout: 1000 })
  } catch {
    // Ignore errors if the marker is not present
  }

  while (Date.now() - start < timeout) {
    const isIdle = await page.evaluate(() => {
      // SONAR suggest 'Prefer `globalThis` over `window`'
      const map = (globalThis as any).mapInstance
      if (!map) {
        console.warn('map instance not found on globalThis')
        return true
      }
      return !(map.isMoving() || map.isZooming())
    })

    if (isIdle) {
      await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))))
      return
    }

    await page.waitForTimeout(100)
  }

  console.warn('Timed out waiting for the map to become idle')
}

/**
 * Additional stronger wait that checks Mapbox tile images are loaded.
 */
export async function waitForMapFullyRendered(page: Page, timeout = 4000): Promise<void> {
  const start = Date.now()
  // first wait for the simple idle marker if present
  try {
    await page.getByTestId('map-idle').waitFor({ timeout: 1000 })
  } catch {
    // ignore - we'll still poll below
  }

  // wait until any <img> tiles are complete (or there are none)
  await page.waitForFunction(
    () => {
      const imgs = Array.from(document.querySelectorAll('.mapboxgl-tiles img')) as HTMLImageElement[]
      if (imgs.length === 0) {
        return true
      }
      return imgs.every((i) => i.complete && (i.naturalWidth ?? 0) > 0)
    },
    { timeout: timeout / 2 },
  )

  // Wait for MapboxGL to finish moving/zooming
  await page
    .waitForFunction(
      () => {
        // @ts-expect-error - exposed in our app
        // SONAR suggest 'Prefer `globalThis` over `window`'
        const map = globalThis.mapInstance
        if (!map) {
          console.warn('map instance not found on globalThis')
          return true
        } // fallback if not exposed
        return !(map.isMoving() || map.isZooming())
      },
      { timeout: timeout / 2 },
    )
    .catch(() => {
      /* ignore if not exposed */
    })

  // give one final animation frame for the canvas to settle
  await page.evaluate(
    () =>
      new Promise((resolve) => {
        requestAnimationFrame(() => requestAnimationFrame(resolve))
      }),
  )

  // guard: if we took too long, still return (test should fail elsewhere)
  if (Date.now() - start > timeout) {
    return
  }
}
