"""
Step 28 — First-load network failure: clear error + in-app retry

On a first load nothing is cached yet, so the DB worker downloads sqlite3.wasm
over the network. These tests simulate that download failing and verify the
app shows a network-specific error with a working "Try again" button instead
of the misleading "browser not compatible" dead end.

The wasm-route tests are Chromium-only: Playwright cannot intercept requests
made inside a dedicated worker on Firefox, so the download failure cannot be
simulated there. The retry machinery itself is covered on both browsers by
test_worker_load_failure_retry, which breaks worker creation instead.

- test_wasm_fetch_failure_shows_network_error (chromium): blocking
  sqlite3.wasm shows the db-error overlay tagged DB_LOAD_FAILED with a retry
  button
- test_retry_recovers_once_network_is_back (chromium): unblocking the request
  and clicking "Try again" initializes the app in place, without a page reload
- test_retry_fails_again_while_still_offline (chromium): retrying with the
  network still down shows the error overlay again (retry stays available)
- test_worker_load_failure_retry (both browsers): a DB worker that fails to
  load shows the error overlay with retry; fixing the worker and clicking
  "Try again" brings the app up in place
"""
import pytest
from playwright.sync_api import Browser, Page, expect

WASM_ROUTE = "**/sqlite3.wasm"

# Wrap window.Worker so the DB worker initially points at a 404 URL (the
# worker fails to load, like on a broken network). window.__fixWorker()
# restores normal behaviour for the retry.
_BREAK_WORKER = """
(() => {
  const RealWorker = window.Worker;
  let broken = true;
  window.__fixWorker = () => { broken = false; };
  window.Worker = function (url, opts) {
    let target = url;
    if (broken) {
      // Corrupt the pathname (not the raw string: Vite dev appends a query
      // string, and appending there would leave the path resolvable)
      const u = new URL(url, window.location.href);
      u.pathname += '.broken-by-test';
      target = u;
    }
    return new RealWorker(target, opts);
  };
})();
"""


@pytest.fixture
def wasm_blocked_page(browser: Browser):
    """A page in its own context (service workers blocked so route interception
    reliably sees the worker's wasm fetch) where sqlite3.wasm requests fail."""
    context = browser.new_context(
        viewport={"width": 1280, "height": 720},
        service_workers="block",
    )
    page = context.new_page()
    page.route(WASM_ROUTE, lambda route: route.abort())
    yield page
    context.close()


def _wait_for_error_overlay(page: Page):
    overlay = page.locator('[data-testid="db-error"]')
    expect(overlay).to_be_visible(timeout=20000)
    return overlay


@pytest.mark.only_browser("chromium")
def test_wasm_fetch_failure_shows_network_error(wasm_blocked_page, app_url: str):
    """A failed sqlite3.wasm download shows the error overlay with the
    network-specific message (DB_LOAD_FAILED) and a retry button."""
    page = wasm_blocked_page
    page.goto(app_url)

    _wait_for_error_overlay(page)

    err = page.evaluate("document.documentElement.getAttribute('data-db-error')")
    assert err and err.startswith("DB_LOAD_FAILED"), (
        f"Expected a DB_LOAD_FAILED tagged error, got: {err!r}"
    )
    expect(page.locator('[data-testid="db-retry"]')).to_be_visible()


@pytest.mark.only_browser("chromium")
def test_retry_recovers_once_network_is_back(wasm_blocked_page, app_url: str):
    """Unblocking the wasm request and clicking 'Try again' brings the app up
    in place — no page reload involved."""
    page = wasm_blocked_page
    page.goto(app_url)
    _wait_for_error_overlay(page)

    # Marker survives only if no navigation/reload happens during the retry
    page.evaluate("window.__retryMarker = true")

    page.unroute(WASM_ROUTE)
    page.locator('[data-testid="db-retry"]').click()

    page.wait_for_function(
        "document.documentElement.getAttribute('data-db-ready') === 'true'",
        timeout=20000,
    )
    expect(page.locator('[data-testid="db-error"]')).not_to_be_visible()
    assert page.evaluate("window.__retryMarker") is True, (
        "The retry must recover in place, not through a page reload"
    )


@pytest.mark.only_browser("chromium")
def test_retry_fails_again_while_still_offline(wasm_blocked_page, app_url: str):
    """Retrying while the network is still down fails again and keeps the
    retry button available."""
    page = wasm_blocked_page
    page.goto(app_url)
    _wait_for_error_overlay(page)

    page.locator('[data-testid="db-retry"]').click()

    # The overlay disappears (loading spinner) then comes back with the button
    _wait_for_error_overlay(page)
    expect(page.locator('[data-testid="db-retry"]')).to_be_visible()


def test_worker_load_failure_retry(page: Page, app_url: str):
    """Cross-browser: a DB worker that fails to load shows the error overlay
    with a retry button, and retrying after the 'network' recovers initializes
    the app in place."""
    page.add_init_script(_BREAK_WORKER)
    page.goto(app_url)

    _wait_for_error_overlay(page)
    expect(page.locator('[data-testid="db-retry"]')).to_be_visible()

    page.evaluate("window.__retryMarker = true")
    page.evaluate("window.__fixWorker()")
    page.locator('[data-testid="db-retry"]').click()

    page.wait_for_function(
        "document.documentElement.getAttribute('data-db-ready') === 'true'",
        timeout=20000,
    )
    expect(page.locator('[data-testid="db-error"]')).not_to_be_visible()
    assert page.evaluate("window.__retryMarker") is True, (
        "The retry must recover in place, not through a page reload"
    )
