import io

from rich.console import Console

from machine_backup.console import Reporter, SummaryRow, mode_from_flag
from machine_backup.modules.base import Action, ActionState, Progress


def _reporter(mode, **kw):
    buf = io.StringIO()
    console = Console(file=buf, force_terminal=(mode == "rich"), width=100)
    return Reporter(mode=mode, console=console, **kw), buf


def test_forced_modes():
    assert _reporter("rich")[0].is_rich is True
    assert _reporter("linear")[0].is_rich is False


def test_auto_non_tty_is_linear():
    r, _ = _reporter("auto")
    assert r.is_rich is False


def test_vv_forces_linear_even_in_rich_capable_console():
    buf = io.StringIO()
    console = Console(file=buf, force_terminal=True, width=100)
    r = Reporter(mode="auto", verbosity=2, console=console)
    assert r.is_rich is False


def test_mode_from_flag():
    assert mode_from_flag(None) == "auto"
    assert mode_from_flag(True) == "rich"
    assert mode_from_flag(False) == "linear"


def test_linear_log_is_timestamped():
    r, buf = _reporter("linear")
    r.log("dump elevaspeed")
    line = buf.getvalue().strip()
    assert "dump elevaspeed" in line
    assert line[:4].isdigit()


def test_summary_contains_all_columns():
    r, buf = _reporter("linear")
    r.summary([SummaryRow("postgres", "ok", 41.2, 52428800)])
    out = buf.getvalue()
    assert "postgres" in out
    assert "ok" in out
    assert "41" in out
    assert "50.0" in out


def test_print_plan_shows_reason_for_destructive():
    r, buf = _reporter("linear")
    r.print_plan(
        [
            Action(
                "postgres",
                ActionState.DESTRUCTIVE,
                "restaurer la base postgres",
                reason="la base existe deja",
            )
        ]
    )
    out = buf.getvalue()
    assert "destructive" in out
    assert "existe deja" in out


def test_print_plan_shows_the_three_states():
    r, buf = _reporter("linear")
    r.print_plan(
        [
            Action("m", ActionState.SATISFIED, "rien a faire"),
            Action("m", ActionState.ADDITIVE, "creer un truc"),
            Action("m", ActionState.DESTRUCTIVE, "ecraser un truc", reason="diverge"),
        ]
    )
    out = buf.getvalue()
    for state in ("satisfied", "additive", "destructive"):
        assert state in out


def test_progress_for_satisfies_module_protocol():
    r, _ = _reporter("linear")
    r.start_module("postgres")
    p = r.progress_for("postgres")
    assert isinstance(p, Progress)
    p.set_total(3)
    p.step("advmfr_hr")
    r.finish_module("postgres", "ok")
    r.close()


def test_linear_progress_logs_each_step():
    r, buf = _reporter("linear")
    r.start_module("postgres", total=2)
    r.advance("postgres", "advmfr_hr")
    r.advance("postgres", "elevaspeed")
    r.finish_module("postgres", "ok")
    r.close()
    out = buf.getvalue()
    assert "advmfr_hr" in out and "elevaspeed" in out


def test_rich_mode_runs_without_error():
    r, buf = _reporter("rich")
    r.start_module("shell", total=2)
    r.advance("shell", "config.fish")
    r.advance("shell", "fish_variables")
    r.finish_module("shell", "ok")
    r.summary([SummaryRow("shell", "ok", 0.4, 1024)])
    r.close()
    assert "shell" in buf.getvalue()
