import json

from osx_backup.context import Context
from osx_backup.modules.base import NullProgress, get_module
from osx_backup.modules.mise import MISE_CONFIG, MiseModule
from osx_backup.runner import RecordingRunner

LS_JSON = json.dumps({"node": [{"version": "26.8.1"}], "bun": [{"version": "1.4.0"}]})


def _seed(ctx: Context) -> None:
    cfg = ctx.home / MISE_CONFIG
    cfg.parent.mkdir(parents=True)
    cfg.write_text('[tools]\nnode = "latest"\n')


def test_registered() -> None:
    assert isinstance(get_module("mise"), MiseModule)


def test_inventory_and_backup(ctx: Context) -> None:
    _seed(ctx)
    r = RecordingRunner()
    r.add(("mise", "ls", "--json"), stdout=LS_JSON)
    ctx.runner = r
    m = MiseModule()
    inv = m.inventory(ctx)
    assert inv.available and inv.items == 2
    res = m.backup(ctx, NullProgress())
    assert res.status == "ok"
    assert (ctx.dest / "mise" / "config.toml").read_text().startswith("[tools]")
    assert json.loads((ctx.dest / "mise" / "ls.json").read_text())["bun"][0]["version"] == "1.4.0"


def test_inventory_without_config(ctx: Context) -> None:
    inv = MiseModule().inventory(ctx)
    assert not inv.available and "config.toml" in inv.reason


def test_inventory_without_mise(ctx: Context) -> None:
    ctx.runner = RecordingRunner(which_map={})
    inv = MiseModule().inventory(ctx)
    assert not inv.available and "mise" in inv.reason


def test_backup_without_config_skipped(ctx: Context) -> None:
    res = MiseModule().backup(ctx, NullProgress())
    assert res.status == "skipped" and MISE_CONFIG in res.message
    assert not (ctx.dest / "mise").exists()


def test_restore_places_config_then_installs(ctx: Context) -> None:
    (ctx.dest / "mise").mkdir()
    (ctx.dest / "mise" / "config.toml").write_text("[tools]\n")
    r = RecordingRunner()
    ctx.runner = r
    res = MiseModule().restore(ctx, NullProgress())
    assert res.status == "ok"
    assert (ctx.home / MISE_CONFIG).read_text() == "[tools]\n"
    assert r.mutations == [
        ["mise", "trust", str(ctx.home / MISE_CONFIG)],
        ["mise", "install", "--yes"],
    ]


def test_restore_without_config_skipped(ctx: Context) -> None:
    assert MiseModule().restore(ctx, NullProgress()).status == "skipped"


def test_restore_install_failure(ctx: Context) -> None:
    (ctx.dest / "mise").mkdir()
    (ctx.dest / "mise" / "config.toml").write_text("[tools]\n")
    r = RecordingRunner()
    r.add(("mise", "install"), returncode=1, stderr="boom")
    ctx.runner = r
    assert MiseModule().restore(ctx, NullProgress()).status == "failed"
