"""Tests for ``mgf-fed start`` — the single federation front-door.

``start`` is a print-only dispatcher over the four onboarding prompts. These
tests pin its ROUTING (which recipe stem it asks for, per mode + detection) and
its menu behaviour — decoupled from the recipe *bodies* (those live in
mgf-standard and are covered by ``test_recipe_prompt`` / ``test_sync_prompt``).
"""

from __future__ import annotations

import argparse
import io
from contextlib import redirect_stderr, redirect_stdout
from pathlib import Path

from mgf.common.cli._exit_codes import EXIT_SUCCESS

import mgf.fed.cli._start as start_mod
from mgf.fed.cli._start import run_start


def _args(**kw: object) -> argparse.Namespace:
    defaults: dict[str, object] = {"mode": None, "path": None, "list": False}
    defaults.update(kw)
    return argparse.Namespace(**defaults)


def _record_recipes(monkeypatch) -> list[str]:
    """Replace ``_print_recipe`` with a recorder; return the captured stems."""
    seen: list[str] = []

    def _fake(stem: str) -> int:
        seen.append(stem)
        return EXIT_SUCCESS

    monkeypatch.setattr(start_mod, "_print_recipe", _fake)
    return seen


class TestModeRouting:
    """--mode <x> dispatches to exactly that recipe, no detection."""

    def test_each_mode_routes_to_its_recipe(self, monkeypatch) -> None:
        expected = {
            "new": "federation-new-project-prompt",
            "onboard": "federation-onboard-prompt",
            "sync": "federation-sync-prompt",
            "adopt": "federation-adopt-prompt",
        }
        for mode, stem in expected.items():
            seen = _record_recipes(monkeypatch)
            rc = run_start(_args(mode=mode))
            assert rc == EXIT_SUCCESS
            assert seen == [stem], f"mode {mode!r} should route to {stem!r}, got {seen}"


class TestMemberDetection:
    """A repo whose [project].name is a registry member auto-routes to sync."""

    def test_member_routes_to_sync(self, monkeypatch, tmp_path: Path) -> None:
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "mgf-foo"\nversion = "0.1.0"\n', encoding="utf-8"
        )
        monkeypatch.setattr(start_mod, "_member_names", lambda: frozenset({"mgf-foo"}))
        seen = _record_recipes(monkeypatch)
        err = io.StringIO()
        with redirect_stderr(err):
            rc = run_start(_args(path=tmp_path))
        assert rc == EXIT_SUCCESS
        assert seen == ["federation-sync-prompt"]
        assert "detected federation member 'mgf-foo'" in err.getvalue()

    def test_non_member_shows_menu_not_a_recipe(self, monkeypatch, tmp_path: Path) -> None:
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "totally-not-mgf"\nversion = "0.1.0"\n', encoding="utf-8"
        )
        monkeypatch.setattr(start_mod, "_member_names", lambda: frozenset())
        seen = _record_recipes(monkeypatch)
        out = io.StringIO()
        with redirect_stdout(out):
            rc = run_start(_args(path=tmp_path))
        assert rc == EXIT_SUCCESS
        assert seen == []  # no prompt dispatched
        text = out.getvalue()
        assert "NOT a federation member" in text
        for mode in ("new", "onboard", "sync", "adopt"):
            assert f"--mode {mode}" in text


class TestMenu:
    def test_list_prints_menu_without_detection(self, monkeypatch, tmp_path: Path) -> None:
        seen = _record_recipes(monkeypatch)
        out = io.StringIO()
        with redirect_stdout(out):
            rc = run_start(_args(path=tmp_path, list=True))
        assert rc == EXIT_SUCCESS
        assert seen == []
        text = out.getvalue()
        for mode in ("new", "onboard", "sync", "adopt"):
            assert f"--mode {mode}" in text

    def test_empty_repo_hint(self, monkeypatch, tmp_path: Path) -> None:
        monkeypatch.setattr(start_mod, "_member_names", lambda: frozenset())
        out = io.StringIO()
        with redirect_stdout(out):
            run_start(_args(path=tmp_path))  # empty tmp_path → no pyproject
        assert "empty/greenfield repo" in out.getvalue()

    def test_consumer_hint(self, monkeypatch, tmp_path: Path) -> None:
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "downstream-app"\nversion = "0.1.0"\n'
            'dependencies = ["mgf-common>=0.47"]\n',
            encoding="utf-8",
        )
        monkeypatch.setattr(start_mod, "_member_names", lambda: frozenset())
        out = io.StringIO()
        with redirect_stdout(out):
            run_start(_args(path=tmp_path))
        assert "consumer" in out.getvalue()


class TestPrintOnly:
    """Contract: start never writes to the filesystem."""

    def test_no_filesystem_writes(self, monkeypatch, tmp_path: Path) -> None:
        monkeypatch.setattr(start_mod, "_member_names", lambda: frozenset())
        before = set(tmp_path.iterdir())
        out = io.StringIO()
        with redirect_stdout(out):
            run_start(_args(path=tmp_path))
        assert set(tmp_path.iterdir()) == before  # nothing created
