import os

import pytest

from machine_backup.logs import CommandResult
from machine_backup.modules import homebrew as brew_module
from machine_backup.modules.base import ActionState, ModuleStatus
from machine_backup.modules.homebrew import HomebrewModule, parse_brewfile

from conftest import make_runner

BREWFILE = """# commentaire
tap "homebrew/services"
brew "postgresql@18", restart_service: true
brew "fish"
cask "raycast"
mas "Xcode", id: 497799835
"""


@pytest.fixture
def fake_brew(monkeypatch):
    monkeypatch.setattr(brew_module, "_find_brew", lambda: "/fake/bin/brew")


def _module(responses=None):
    runner = make_runner(responses)
    return HomebrewModule(runner=runner), runner


def test_parse_brewfile():
    entries = parse_brewfile(BREWFILE)
    assert entries.taps == ["homebrew/services"]
    assert entries.formulae == ["postgresql@18", "fish"]
    assert entries.casks == ["raycast"]
    assert entries.mas == ["Xcode"]


def test_parse_brewfile_ignores_comments_and_blanks():
    entries = parse_brewfile('\n# brew "pas-installe"\n\nbrew "reel"\n')
    assert entries.formulae == ["reel"]


def test_check_reports_missing_brew(monkeypatch):
    monkeypatch.setattr(brew_module, "_find_brew", lambda: None)
    problems = HomebrewModule().check()
    assert problems and problems[0].fatal


def test_collect_dumps_brewfile(tmp_path, fake_brew):
    mod, runner = _module()
    dest = tmp_path / "homebrew"
    result = mod.collect(dest)

    (call,) = [c for c in runner.calls if os.path.basename(c[0]) == "brew"]
    assert call[1:3] == ["bundle", "dump"]
    assert "--describe" in call
    assert str(dest / "Brewfile") in call
    assert result.status is ModuleStatus.OK


def test_collect_artifacts_are_not_sensitive(tmp_path, fake_brew):
    mod, _ = _module()
    dest = tmp_path / "homebrew"
    dest.mkdir(parents=True)
    (dest / "Brewfile").write_text(BREWFILE)
    result = mod.collect(dest)
    assert result.artifacts
    assert not any(a.sensitive for a in result.artifacts)


def _installed(taps="homebrew/services\n", formulae="fish\n", casks=""):
    """Reponses de brew pour l'etat installe."""
    calls = {"tap": taps, "formula": formulae, "cask": casks}

    def runner(argv, **kwargs):
        argv = [str(a) for a in argv]
        runner.calls.append(argv)
        if argv[1:2] == ["tap"]:
            return CommandResult(argv=argv, returncode=0, stdout=calls["tap"])
        if argv[1:2] == ["list"] and "--formula" in argv:
            return CommandResult(argv=argv, returncode=0, stdout=calls["formula"])
        if argv[1:2] == ["list"] and "--cask" in argv:
            return CommandResult(argv=argv, returncode=0, stdout=calls["cask"])
        return CommandResult(argv=argv, returncode=0)

    runner.calls = []
    return runner


def test_plan_restore_satisfied_vs_additive(tmp_path, fake_brew):
    src = tmp_path / "homebrew"
    src.mkdir()
    (src / "Brewfile").write_text(BREWFILE)

    mod = HomebrewModule(runner=_installed())
    actions = mod.plan_restore(src)
    by_target = {a.target: a.state for a in actions}

    assert by_target["fish"] is ActionState.SATISFIED
    assert by_target["homebrew/services"] is ActionState.SATISFIED
    assert by_target["postgresql@18"] is ActionState.ADDITIVE
    assert by_target["raycast"] is ActionState.ADDITIVE
    # Le restore homebrew est strictement additif.
    assert not any(a.state is ActionState.DESTRUCTIVE for a in actions)


def test_plan_restore_no_brewfile_returns_empty(tmp_path, fake_brew):
    mod, _ = _module()
    assert mod.plan_restore(tmp_path / "vide") == []


def test_restore_never_uses_cleanup(tmp_path, fake_brew):
    src = tmp_path / "homebrew"
    src.mkdir()
    (src / "Brewfile").write_text(BREWFILE)
    runner = _installed()
    mod = HomebrewModule(runner=runner)
    actions = mod.plan_restore(src)
    mod.restore(src, actions, force=False)

    assert not any("--cleanup" in c for call in runner.calls for c in call)


def test_restore_even_with_force_never_uses_cleanup(tmp_path, fake_brew):
    src = tmp_path / "homebrew"
    src.mkdir()
    (src / "Brewfile").write_text(BREWFILE)
    runner = _installed()
    mod = HomebrewModule(runner=runner)
    mod.restore(src, mod.plan_restore(src), force=True)
    assert not any("--cleanup" in c for call in runner.calls for c in call)


def test_restore_runs_bundle_install_when_something_is_missing(tmp_path, fake_brew):
    src = tmp_path / "homebrew"
    src.mkdir()
    (src / "Brewfile").write_text(BREWFILE)
    runner = _installed()
    mod = HomebrewModule(runner=runner)
    result = mod.restore(src, mod.plan_restore(src), force=False)

    installs = [c for c in runner.calls if c[1:3] == ["bundle", "install"]]
    assert len(installs) == 1
    assert result.status is ModuleStatus.OK


def test_restore_all_satisfied_runs_nothing(tmp_path, fake_brew):
    src = tmp_path / "homebrew"
    src.mkdir()
    (src / "Brewfile").write_text('brew "fish"\n')
    runner = _installed(formulae="fish\n")
    mod = HomebrewModule(runner=runner)
    mod.restore(src, mod.plan_restore(src), force=False)

    assert not any(c[1:3] == ["bundle", "install"] for c in runner.calls)
