"""
Backlog — Manual "Check for updates" button in Settings

- test_check_for_update_button_visible: The button renders in the "Check Updates" section
- test_check_for_update_calls_registration_update: Clicking the button forces a re-check (registration.update())
- test_check_for_update_shows_up_to_date_when_nothing_found: No update found -> a transient "up to date" message appears, then disappears
- test_check_for_update_found_still_offers_choice: A manual check that finds an update still shows the Update now / Later modal, not an auto-apply
- test_up_to_date_message_not_shown_when_update_found: The "up to date" heuristic does not fire when a check does find an update
- test_last_check_date_shown_never_checked_initially: Fresh DB shows "never checked manually"
- test_last_check_date_updates_after_click_and_persists: Clicking the button stamps the last-checked date, which survives a reload

Uses the dev-only test hook `window.__appUpdateTest.mockCheckFn` (see
src/composables/useAppUpdate.js) as a stand-in for `registration.update()` —
simulating a real service-worker update in Playwright would require two
production builds (see Step 24). page.clock drives the "no update found"
timeout heuristic deterministically (as in Step 15/26).
"""
import re

from playwright.sync_api import Page, expect


def _wait_for_db(page: Page, app_url: str):
    page.goto(app_url)
    page.wait_for_function(
        "document.documentElement.getAttribute('data-db-ready') === 'true'",
        timeout=15000,
    )


def _go_to_settings(page: Page, app_url: str):
    """Navigate to Settings, waiting for DB and the dev test hook to be ready."""
    _wait_for_db(page, app_url)
    page.click('a[href="#/settings"]')
    page.wait_for_selector('[data-testid="settings-language"]')
    page.wait_for_function("!!window.__appUpdateTest", timeout=10000)


def _mock_check_fn_no_update(page: Page):
    """Mock registration.update() as a no-op: the check resolves but nothing changes."""
    page.evaluate("""() => {
        window.__checkCalls = []
        window.__appUpdateTest.mockCheckFn = () => {
            window.__checkCalls.push(true)
            return Promise.resolve()
        }
    }""")


def _mock_check_fn_found_update(page: Page):
    """Mock registration.update() as if it found a new SW: flips `needRefresh`."""
    page.evaluate("""() => {
        window.__checkCalls = []
        window.__appUpdateTest.mockCheckFn = () => {
            window.__checkCalls.push(true)
            window.__appUpdateTest.trigger()
            return Promise.resolve()
        }
    }""")


def test_check_for_update_button_visible(page: Page, app_url: str):
    """The 'Check for updates' button renders in the new Application card."""
    _go_to_settings(page, app_url)
    expect(page.locator('[data-testid="settings-check-for-update"]')).to_be_visible()


def test_check_for_update_calls_registration_update(page: Page, app_url: str):
    """Clicking the button calls through to registration.update()."""
    _go_to_settings(page, app_url)
    _mock_check_fn_no_update(page)

    page.locator('[data-testid="settings-check-for-update"]').click()

    calls = page.evaluate("window.__checkCalls")
    assert calls == [True], f"registration.update() should have been called, got {calls}"


def test_check_for_update_shows_up_to_date_when_nothing_found(page: Page, app_url: str):
    """No update found: a transient 'up to date' message appears, then disappears."""
    page.clock.install()
    _go_to_settings(page, app_url)
    _mock_check_fn_no_update(page)

    up_to_date = page.locator('[data-testid="settings-up-to-date"]')
    expect(up_to_date).not_to_be_visible()

    page.locator('[data-testid="settings-check-for-update"]').click()
    expect(up_to_date).not_to_be_visible()  # not shown immediately

    page.clock.run_for(2100)
    page.wait_for_timeout(100)
    expect(up_to_date).to_be_visible()

    page.clock.run_for(3100)
    page.wait_for_timeout(100)
    expect(up_to_date).not_to_be_visible()


def test_check_for_update_found_still_offers_choice(page: Page, app_url: str):
    """A manual check that finds an update still shows Update now / Later — updating stays optional."""
    page.clock.install()
    _go_to_settings(page, app_url)
    _mock_check_fn_found_update(page)

    page.locator('[data-testid="settings-check-for-update"]').click()

    modal = page.locator('[data-testid="app-update-modal"]')
    expect(modal).to_be_visible()
    expect(page.locator('[data-testid="app-update-now"]')).to_be_visible()
    expect(page.locator('[data-testid="app-update-later"]')).to_be_visible()


def test_up_to_date_message_not_shown_when_update_found(page: Page, app_url: str):
    """The 'up to date' heuristic does not fire when a check does find an update."""
    page.clock.install()
    _go_to_settings(page, app_url)
    _mock_check_fn_found_update(page)

    page.locator('[data-testid="settings-check-for-update"]').click()
    expect(page.locator('[data-testid="app-update-modal"]')).to_be_visible()

    page.clock.run_for(2100)
    page.wait_for_timeout(100)
    expect(page.locator('[data-testid="settings-up-to-date"]')).not_to_be_visible()


def test_last_check_date_shown_never_checked_initially(page: Page, app_url: str):
    """A fresh DB has never been checked manually, so the fallback text is shown."""
    _go_to_settings(page, app_url)
    last_check = page.locator('[data-testid="settings-last-update-check"]')
    expect(last_check).to_be_visible()
    expect(last_check).to_have_text("Never checked manually")


def test_last_check_date_updates_after_click_and_persists(page: Page, app_url: str):
    """Clicking the button stamps a last-checked date, which survives a reload."""
    _go_to_settings(page, app_url)
    _mock_check_fn_no_update(page)

    last_check = page.locator('[data-testid="settings-last-update-check"]')
    expect(last_check).to_have_text("Never checked manually")

    page.locator('[data-testid="settings-check-for-update"]').click()
    expect(last_check).to_have_text(re.compile(r"^Last manual check: "))

    page.reload()
    page.wait_for_selector('[data-testid="settings-language"]')
    expect(last_check).to_have_text(re.compile(r"^Last manual check: "))
