import json
import subprocess
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
RECONNECT = ROOT / "frontend" / "reconnect-outboxes.js"


def run_node(script: str) -> dict:
    result = subprocess.run(
        ["node", "-e", script], cwd=ROOT, text=True, capture_output=True, check=True
    )
    return json.loads(result.stdout)


def test_successful_reconnect_restores_identity_and_flushes_unchanged_outboxes():
    result = run_node(
        f"""
const createReconnectOutboxes = require({json.dumps(str(RECONNECT))});
const calls = [];
const reconnect = createReconnectOutboxes({{
  refresh: async () => ({{context:{{user:{{login:'timmy'}}}}}}),
  restoreIdentity: login => calls.push(['identity', login]),
  flushIssue: () => calls.push(['issue']),
  flushAuthored: () => calls.push(['authored']),
  flushNotificationReads: () => calls.push(['notification-read']),
}});
reconnect().then(result => process.stdout.write(JSON.stringify({{result, calls}})));
"""
    )

    assert result == {
        "result": True,
        "calls": [
            ["identity", "timmy"],
            ["issue"],
            ["authored"],
            ["notification-read"],
        ],
    }


def test_concurrent_reconnect_requests_queue_one_follow_up_flush():
    result = run_node(
        f"""
const createReconnectOutboxes = require({json.dumps(str(RECONNECT))});
(async () => {{
let releaseFirst; let issueCalls=0; let active=0; let maxActive=0;
const reconnect = createReconnectOutboxes({{
  refresh: async () => ({{context:{{user:{{login:'timmy'}}}}}}),
  restoreIdentity: () => {{}},
  flushIssue: async () => {{
    issueCalls++; active++; maxActive=Math.max(maxActive,active);
    if (issueCalls === 1) await new Promise(resolve => {{ releaseFirst=resolve; }});
    active--;
  }},
  flushAuthored: async () => {{}},
  flushNotificationReads: async () => {{}},
}});
const first=reconnect();
while (!releaseFirst) await new Promise(resolve=>setTimeout(resolve,0));
const second=reconnect();
releaseFirst();
await Promise.all([first,second]);
process.stdout.write(JSON.stringify({{issueCalls,maxActive}}));
}})().catch(error => {{ console.error(error); process.exit(1); }});
"""
    )

    assert result == {"issueCalls": 2, "maxActive": 1}


def test_dashboard_uses_reconnect_flush_after_live_identity_refresh():
    index = (ROOT / "frontend" / "index.html").read_text()
    dashboard = (ROOT / "frontend" / "dashboard.js").read_text()

    assert '<script src="static/reconnect-outboxes.js"></script>' in index
    assert "const reconnectOutboxes = createReconnectOutboxes({" in dashboard
    assert "restoreIdentity: login => { activeFlushLogin = login; confirmedOwnerLogin = login; }" in dashboard
    assert "window.addEventListener('online', reconnectAfterOnline);" in dashboard
    assert "setTimeout(reconnectOutboxes, 500)" in dashboard


def test_dashboard_registers_reconnect_before_offline_startup_can_wait():
    dashboard = (ROOT / "frontend" / "dashboard.js").read_text()

    listener = dashboard.index("window.addEventListener('online', reconnectAfterOnline);")
    offline_startup = dashboard.index("await updateOfflineWorkControls();")

    assert listener < offline_startup
