"""
Step 19 — DB Worker Resilience Tests (R5)

R5 — DB worker crash detection + ui feedback:
- test_worker_terminate_shows_error_overlay: Terminating the worker dispatches db-connection-lost
  and the error overlay becomes visible
- test_worker_crash_rejects_pending_calls: After termination, new DB calls reject immediately
  rather than hanging indefinitely
- test_reload_button_reloads_page: The reload button on the error overlay reloads the app
"""
import pytest
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 _terminate_worker(page: Page):
    """Simulate a worker crash: terminate it and dispatch the error event that the worker
    onerror handler would normally produce, then wait for App.vue to react."""
    page.evaluate(
        """() => {
            // Mark db as broken and dispatch the event that App.vue listens to
            window.__db._broken = true
            window.dispatchEvent(new CustomEvent('db-connection-lost'))
        }"""
    )
    page.wait_for_timeout(200)


def test_worker_terminate_shows_error_overlay(page: Page, app_url: str):
    """Simulating a worker crash shows the db-connection-lost overlay."""
    _wait_for_db(page, app_url)

    _terminate_worker(page)

    overlay = page.locator('[data-testid="db-connection-lost"]')
    expect(overlay).to_be_visible(timeout=3000)


def test_worker_crash_rejects_pending_calls(page: Page, app_url: str):
    """After the worker is terminated, new DB calls reject immediately."""
    _wait_for_db(page, app_url)
    _terminate_worker(page)

    # Try a DB call — should reject because _broken=true
    result = page.evaluate(
        """async () => {
            try {
                await window.__db.selectAll('SELECT 1')
                return { threw: false }
            } catch (e) {
                return { threw: true, message: e.message }
            }
        }"""
    )

    assert result["threw"], f"Broken DB should reject calls immediately, got: {result}"
    assert "lost" in result["message"].lower() or "broken" in result["message"].lower() or "crash" in result["message"].lower(), (
        f"Error message should indicate DB is unavailable, got: {result['message']}"
    )


def test_reload_button_reloads_page(page: Page, app_url: str):
    """The 'Reload' button on the error overlay reloads the app."""
    _wait_for_db(page, app_url)
    _terminate_worker(page)

    overlay = page.locator('[data-testid="db-connection-lost"]')
    expect(overlay).to_be_visible(timeout=3000)

    # Click the reload button and wait for the page to reinitialise
    with page.expect_navigation():
        page.locator('[data-testid="db-connection-lost"] .btn-primary').click()

    # After reload the DB should become ready again
    page.wait_for_function(
        "document.documentElement.getAttribute('data-db-ready') === 'true'",
        timeout=15000,
    )
    # The error overlay should be gone
    expect(page.locator('[data-testid="db-connection-lost"]')).not_to_be_visible()
