import json
import subprocess
from pathlib import Path

import pytest

from tests.dashboard_bundle import dashboard


ROOT = Path(__file__).resolve().parents[1]
REPLAN = ROOT / "frontend" / "agenda-replan.js"


def run_node(script):
    result = subprocess.run(["node", "-e", script], capture_output=True, text=True)
    assert result.returncode == 0, result.stderr
    return json.loads(result.stdout)


def test_overdue_replan_sweep_orders_items_and_only_advances_after_confirmed_change():
    script = f"""
const createSweep = require({json.dumps(str(REPLAN))});
const calls = [];
let fail = true;
const items = [
  {{key:'o/r#3', repository:'o/r', number:3, due_date:'2026-08-11T18:00:00Z'}},
  {{key:'o/r#1', repository:'o/r', number:1, due_date:'2026-08-09T18:00:00Z'}},
  {{key:'o/r#2', repository:'o/r', number:2, due_date:'2026-08-10T18:00:00Z'}},
];
const sweep = createSweep({{
  now: () => new Date('2026-08-12T12:00:00'),
  update: async (item, due) => {{ calls.push([item.key, due]); if (fail) throw new Error('offline'); return {{due_date:due}}; }},
}});
async function run() {{
  const started = sweep.start(items);
  const failed = await sweep.tomorrow();
  const afterFailure = sweep.snapshot();
  fail = false;
  const changed = await sweep.tomorrow();
  const afterChange = sweep.snapshot();
  const kept = await sweep.keep();
  process.stdout.write(JSON.stringify({{started, failed, afterFailure, changed, afterChange, kept, calls}}));
}}
run();
"""
    assert run_node(script) == {
        "started": {"active": True, "index": 0, "total": 3, "current": "o/r#1", "pending": False},
        "failed": {"ok": False, "error": "offline"},
        "afterFailure": {"active": True, "index": 0, "total": 3, "current": "o/r#1", "pending": False},
        "changed": {"ok": True, "done": False},
        "afterChange": {"active": True, "index": 1, "total": 3, "current": "o/r#2", "pending": False},
        "kept": {"ok": True, "done": False},
        "calls": [["o/r#1", "2026-08-13T23:59:59Z"], ["o/r#1", "2026-08-13T23:59:59Z"]],
    }


def test_overdue_replan_rejects_non_future_choice_and_prevents_parallel_updates():
    script = f"""
const createSweep = require({json.dumps(str(REPLAN))});
let resolveUpdate;
let calls = 0;
const sweep = createSweep({{
  now: () => new Date('2026-08-12T12:00:00'),
  update: async () => {{ calls += 1; await new Promise(resolve => resolveUpdate = resolve); return {{}}; }},
}});
async function run() {{
  sweep.start([{{key:'o/r#1', due_date:'2026-08-10T18:00:00Z'}}]);
  const invalid = await sweep.choose('2026-08-12');
  const first = sweep.choose('2026-08-14');
  const duplicate = await sweep.choose('2026-08-15');
  resolveUpdate();
  const confirmed = await first;
  process.stdout.write(JSON.stringify({{invalid, duplicate, confirmed, calls, final:sweep.snapshot()}}));
}}
run();
"""
    assert run_node(script) == {
        "invalid": {"ok": False, "error": "Choose a future date."},
        "duplicate": {"ok": False, "error": "Deadline update already in progress."},
        "confirmed": {"ok": True, "done": True},
        "calls": 1,
        "final": {"active": False, "index": 1, "total": 1, "current": None, "pending": False},
    }


def test_overdue_replan_orders_by_calendar_day_without_timezone_drift():
    script = f"""
const createSweep = require({json.dumps(str(REPLAN))});
const sweep = createSweep({{update:async()=>({{}})}});
const started = sweep.start([
  {{key:'o/r#2',repository:'o/r',number:2,due_date:'2026-08-13T01:00:00Z'}},
  {{key:'o/r#1',repository:'o/r',number:1,due_date:'2026-08-12T23:59:59Z'}},
]);
process.stdout.write(JSON.stringify(started));
"""
    environment = {**__import__('os').environ, "TZ": "Asia/Tokyo"}
    result = subprocess.run(
        ["node", "-e", script], capture_output=True, text=True, env=environment
    )
    assert result.returncode == 0, result.stderr
    assert json.loads(result.stdout)["current"] == "o/r#1"


@pytest.mark.anyio
async def test_mobile_agenda_exposes_thumb_safe_replan_controls_and_wires_existing_mutation():
    html = await dashboard()

    assert 'id="start-agenda-replan"' in html
    assert 'id="agenda-replan-controls"' in html
    assert 'id="agenda-replan-progress"' in html
    assert 'id="agenda-replan-keep"' in html
    assert 'id="agenda-replan-tomorrow"' in html
    assert 'id="agenda-replan-date"' in html
    assert 'id="agenda-replan-choose"' in html
    assert 'id="agenda-replan-cancel"' in html
    assert "agendaReplan.start(overdue)" in html
    assert "issueController.updateDueDate(item, dueDate)" in html
    assert ".agenda-replan-actions button { min-height:44px;" in html
    assert "@media(max-width:360px)" in html
