import json
import subprocess
from pathlib import Path

FRONTEND = Path(__file__).resolve().parents[1] / "frontend"
CONTROLLER = FRONTEND / "mobile-issue-detail-nav.js"


def test_pull_navigation_prepares_review_before_scrolling_and_focuses_reply():
    script = f"""
const createNavigation = require({json.dumps(str(CONTROLLER))});
class FakeElement {{
  constructor(name) {{ this.name=name; this.listeners={{}}; this.attributes={{}}; this.open=false; this.focuses=0; this.scrolls=[]; }}
  addEventListener(name, callback) {{ this.listeners[name]=callback; }}
  removeEventListener(name) {{ delete this.listeners[name]; }}
  setAttribute(name, value) {{ this.attributes[name]=value; }}
  removeAttribute(name) {{ delete this.attributes[name]; }}
  focus() {{ this.focuses += 1; }}
  scrollIntoView(options) {{ this.scrolls.push({{...options, openWhenScrolled:this.open}}); }}
}}
const buttons = Object.fromEntries(['overview','conversation','reply','review'].map(name => [name,new FakeElement(name)]));
const targets = Object.fromEntries(['overview','conversation','reply','review'].map(name => [name,new FakeElement(name)]));
let prepared = [];
const navigation = createNavigation({{
  buttons, targets,
  beforeNavigate:{{review(target) {{ prepared.push('review'); target.open=true; }}}},
  prefersReducedMotion:() => true,
}});
navigation.start();
navigation.navigate('reply');
navigation.navigate('review');
process.stdout.write(JSON.stringify({{
  replyFocuses:targets.reply.focuses,
  reviewScrolls:targets.review.scrolls,
  prepared,
  current:Object.fromEntries(Object.entries(buttons).map(([name,button]) => [name,button.attributes['aria-current'] || null])),
}}));
"""
    result = subprocess.run(["node", "-e", script], capture_output=True, text=True)

    assert result.returncode == 0, result.stderr
    assert json.loads(result.stdout) == {
        "replyFocuses": 1,
        "reviewScrolls": [{"block": "start", "behavior": "auto", "openWhenScrolled": True}],
        "prepared": ["review"],
        "current": {
            "overview": None,
            "conversation": None,
            "reply": None,
            "review": "location",
        },
    }


def test_pull_sheet_wires_four_workspace_destinations_to_existing_lazy_review():
    html = (FRONTEND / "index.html").read_text()
    dashboard_js = (FRONTEND / "dashboard.js").read_text()

    assert '<nav class="mobile-detail-nav mobile-pull-detail-nav"' in html
    assert 'aria-label="Pull request sections"' in html
    for name, label in (
        ("overview", "Overview"),
        ("conversation", "Conversation"),
        ("reply", "Reply"),
        ("review", "Review &amp; merge"),
    ):
        assert f'data-pull-section="{name}"' in html
        assert f">{label}</button>" in html
    assert 'id="pull-overview"' in html
    assert 'id="pull-conversation"' in html
    assert 'id="pull-comment"' in html
    assert 'id="pull-review"' in html
    assert "document.querySelectorAll('[data-pull-section]')" in dashboard_js
    assert "beforeNavigate:{review(target) { target.open = true; }}" in dashboard_js


def test_pull_workspace_rail_has_phone_containment_and_desktop_visibility_contracts():
    css = (FRONTEND / "dashboard.css").read_text()

    assert "@media (max-width:600px)" in css
    assert ".mobile-issue-detail-nav, .mobile-pull-detail-nav" in css
    assert "position:sticky; top:env(safe-area-inset-top)" in css
    assert "grid-template-columns:repeat(4,minmax(0,1fr))" in css
    assert "min-width:0; min-height:44px" in css
    assert "#pull-overview, #pull-conversation, #pull-comment, #pull-review { scroll-margin-top:72px; }" in css
    assert "@media (min-width:601px) { .mobile-issue-detail-nav, .mobile-pull-detail-nav { display:none; } }" in css
