"""End-to-end Playwright tests for the CodeMirror 6 editor.

The live_server fixture (conftest.py) seeds user "alice"/"secret" and a
single blueprint at id=1. The test logs in, opens the blueprint
detail, and exercises the editor's autocomplete + form-bridge.

Running locally: `uv run pytest -m e2e tests/e2e/test_editor.py`.
Requires `uv run playwright install chromium` once. Under Claude
Code's Bash, pass `dangerouslyDisableSandbox: true` — Chromium's
Mach-port IPC is sandbox-blocked.
"""
from __future__ import annotations

import pytest
from playwright.sync_api import Page, expect

pytestmark = pytest.mark.e2e


def _login(page: Page, base_url: str) -> None:
    page.goto(f"{base_url}/login")
    page.fill('input[name="username"]', "alice")
    page.fill('input[name="password"]', "secret")
    page.click('button[type="submit"]')


def test_blueprint_autocomplete_accept_writes_into_hidden_textarea(page: Page, live_server) -> None:
    """Type a cvar prefix in the editor, accept the popup with Tab, then
    fire a synthetic submit on the form and assert the hidden textarea
    carries the value. Exercises:
      1. cm6 mount + syntax / autocomplete extensions.
      2. /static/data/srccfg-vocab.json lazy fetch.
      3. The submit-time copy bridge in editor.js (the v2 form-bridge
         pattern's failure mode).
    """
    base = live_server["base_url"]
    bp_id = live_server["blueprint_id"]
    _login(page, base)
    page.goto(f"{base}/blueprints/{bp_id}")

    # Wait for cm6 to mount (the bundle is `defer`red and the vocab fetch
    # is async). The .cm-content element is what cm6 renders the
    # editable surface as.
    editor = page.locator(".cm-content")
    expect(editor).to_be_visible(timeout=5000)

    editor.click()
    page.keyboard.type("sv_che")

    # cm6's autocomplete tooltip class.
    popup = page.locator(".cm-tooltip-autocomplete")
    expect(popup).to_be_visible(timeout=3000)
    expect(popup).to_contain_text("sv_cheats")

    # Give cm6 a beat to settle the popup's selectedCompletion state
    # before pressing accept. Without this, the popup is *visible* but
    # selectedCompletion may still be null on the same tick, so
    # acceptCompletion() returns false and Tab falls through to
    # indentWithTab.
    page.wait_for_timeout(200)
    page.keyboard.press("Tab")  # custom Tab→acceptCompletion binding in editor-entry.js

    # Fire submit + immediately read the textarea before the navigation
    # happens, so we can assert the submit-capture handler did its job.
    textarea_value = page.evaluate("""() => {
      const ta = document.querySelector('textarea[name="config"]');
      const form = ta.closest('form');
      // Run capture-phase listeners synchronously (no real submit).
      form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
      return ta.value;
    }""")
    assert "sv_cheats" in textarea_value, f"submit-capture handler did not write into textarea: {textarea_value!r}"


def test_copy_preserves_newlines_across_lines(page: Page, live_server) -> None:
    """Regression gate for bug class 1 from the v1 attempt (Prism+
    contenteditable collapsed multi-line copy). cm6 handles this
    correctly out of the box; this test pins the behavior so a future
    change doesn't regress it. We don't read the actual clipboard
    (permissions are fiddly in CI); Selection.toString() over a
    multi-line selection is enough evidence that cm6's DOM walk
    preserves linebreaks."""
    base = live_server["base_url"]
    bp_id = live_server["blueprint_id"]
    _login(page, base)
    page.goto(f"{base}/blueprints/{bp_id}")

    editor = page.locator(".cm-content")
    expect(editor).to_be_visible(timeout=5000)
    editor.click()
    page.keyboard.type("first line\nsecond line\nthird line")

    # Read the cm6 doc via the per-textarea controller wired by
    # editor.js (textarea.__editorController). This sidesteps both the
    # platform-specific select-all shortcut and the lack of a public
    # path from .cm-content back to the EditorView.
    doc_text = page.evaluate("""() => {
      const ta = document.querySelector('textarea[name="config"]');
      return ta.__editorController.getValue();
    }""")
    assert doc_text.count("\n") >= 2, f"expected ≥2 line breaks in cm6 doc, got: {doc_text!r}"
