import json
import subprocess
from pathlib import Path


MERGER = Path(__file__).parents[1] / "frontend" / "checklist-conflict.js"


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


def test_merge_applies_local_task_delta_to_latest_body_without_reverting_remote_edits():
    script = f"""
const mergeChecklistConflict = require({json.dumps(str(MERGER))});
const result = mergeChecklistConflict({{
  baseBody:'Intro\\n- [ ] Build\\n- [ ] Test',
  localBody:'Intro\\n- [x] Build\\n- [ ] Test',
  remoteBody:'Updated intro\\n- [ ] Test\\n- [ ] Build\\n- [x] Document',
}});
process.stdout.write(JSON.stringify(result));
"""
    output = run_node(script)

    assert output == {
        "body": "Updated intro\n- [ ] Test\n- [x] Build\n- [x] Document",
        "changes": [{"label": "Build", "checked": True}],
        "conflicts": [],
    }


def test_merge_reports_missing_or_ambiguous_changed_tasks_without_writing_a_body():
    script = f"""
const mergeChecklistConflict = require({json.dumps(str(MERGER))});
const missing = mergeChecklistConflict({{
  baseBody:'- [ ] Ship', localBody:'- [x] Ship', remoteBody:'- [ ] Release',
}});
const duplicate = mergeChecklistConflict({{
  baseBody:'- [ ] Ship', localBody:'- [x] Ship', remoteBody:'- [ ] Ship\\n- [ ] Ship',
}});
process.stdout.write(JSON.stringify({{missing,duplicate}}));
"""
    output = run_node(script)

    assert output["missing"]["body"] is None
    assert output["missing"]["conflicts"] == [{"label": "Ship", "reason": "missing"}]
    assert output["duplicate"]["body"] is None
    assert output["duplicate"]["conflicts"] == [{"label": "Ship", "reason": "ambiguous"}]


def test_merge_appends_a_locally_added_step_to_remote_edits_without_reverting_them():
    script = f"""
const mergeChecklistConflict = require({json.dumps(str(MERGER))});
const result = mergeChecklistConflict({{
  baseBody:'Intro\\n- [ ] Build',
  localBody:'Intro\\n- [ ] Build\\n- [ ] Verify rollback',
  remoteBody:'Updated intro\\n- [x] Build\\n- [ ] Notify support',
}});
process.stdout.write(JSON.stringify(result));
"""
    output = run_node(script)

    assert output == {
        "body": "Updated intro\n- [x] Build\n- [ ] Notify support\n- [ ] Verify rollback",
        "changes": [{"label": "Verify rollback", "checked": False, "added": True}],
        "conflicts": [],
    }


def test_merge_rejects_an_ambiguous_locally_added_duplicate():
    script = f"""
const mergeChecklistConflict = require({json.dumps(str(MERGER))});
const result = mergeChecklistConflict({{
  baseBody:'- [ ] Build',
  localBody:'- [ ] Build\\n- [ ] Verify rollback\\n- [ ]  verify   rollback ',
  remoteBody:'- [x] Build',
}});
process.stdout.write(JSON.stringify(result));
"""
    output = run_node(script)

    assert output["body"] is None
    assert output["conflicts"] == [{"label": "Verify rollback", "reason": "ambiguous"}]


def test_merge_replays_rename_and_reorder_intent_onto_unrelated_remote_edits():
    script = f"""
const mergeChecklistConflict = require({json.dumps(str(MERGER))});
const result = mergeChecklistConflict({{
  baseBody:'Intro\\n- [ ] Build\\n- [ ] Test',
  localBody:'Intro\\n- [ ] Test\\n- [ ] Compile',
  remoteBody:'Updated intro\\n- [x] Build\\n- [ ] Test\\nRemote note',
  operations:[
    {{action:'rename',index:0,label:'Compile'}},
    {{action:'move-later',index:0}},
  ],
}});
process.stdout.write(JSON.stringify(result));
"""
    output = run_node(script)

    assert output == {
        "body": "Updated intro\n- [ ] Test\n- [x] Compile\nRemote note",
        "changes": [
            {"label": "Build", "renamed": "Compile"},
            {"label": "Compile", "moved": "later"},
        ],
        "conflicts": [],
    }
