"""Tests for batch-mode defaults in the Janus Labs CLI."""

from __future__ import annotations

import builtins
from contextlib import contextmanager
import hashlib
import json
import shutil
import sys
import uuid
from pathlib import Path
from types import SimpleNamespace

from cli import interactive as interactive_mod
from cli import main as cli_main
from cli import terminal_ui
from harness import agent_adapter
from scaffold import scorer


class Args(SimpleNamespace):
    """Simple argparse-like namespace for CLI command tests."""


def _new_tmp_path() -> Path:
    base_root = Path(__file__).resolve().parents[1] / ".test-artifacts"
    base_root.mkdir(exist_ok=True)
    path = base_root / f"janus-cli-batch-{uuid.uuid4().hex}"
    path.mkdir()
    return path


@contextmanager
def _tmp_path():
    path = _new_tmp_path()
    try:
        yield path
    finally:
        shutil.rmtree(path, ignore_errors=True)


def _stub_suite(tmp_path: Path, behavior_count: int = 1):
    behaviors = []
    for index in range(behavior_count):
        behavior_id = f"BHV-{index + 1:03d}-test-behavior"
        behaviors.append(
            SimpleNamespace(
                behavior_id=behavior_id,
                name=f"Test behavior {index + 1}",
                description=f"Description {index + 1}",
                threshold=7.0,
                rubric={1: "bad", 10: "good"},
            )
        )
    suite = SimpleNamespace(
        suite_id="refactor-storm",
        version="1.0.0",
        comparability_key="refactor-storm:1.0.0",
        behaviors=behaviors,
    )
    workspace_root = tmp_path / "workspace"
    for behavior in behaviors:
        behavior_dir = workspace_root / behavior.behavior_id
        behavior_dir.mkdir(parents=True)
        (behavior_dir / "README.md").write_text(
            f"Do the thing for {behavior.behavior_id}",
            encoding="utf-8",
        )
    return suite, workspace_root


def _patch_full_run(monkeypatch, tmp_path: Path):
    suite, workspace_root = _stub_suite(tmp_path)
    monkeypatch.setattr(cli_main, "get_suite", lambda suite_id: suite)

    def fake_run_agent_on_workspace(adapter, workspace_dir, prompt, behavior_id):
        return agent_adapter.AgentRunResult(
            behavior_id=behavior_id,
            success=True,
            duration_ms=2500,
            stdout="ok",
            stderr="",
            error=None,
        )

    def fake_score_outcome(workspace_dir, behavior_id, threshold, rubric):
        return SimpleNamespace(
            normalized_score=0.82,
            passed_threshold=True,
            git_diff={"patch": f"diff for {behavior_id}", "files_changed": ["src/app.py"]},
            test_results={"output": f"tests for {behavior_id}", "passed": 1, "failed": 0},
            workspace_hash=f"hash-{behavior_id}",
        )

    monkeypatch.setattr(agent_adapter, "run_agent_on_workspace", fake_run_agent_on_workspace)
    monkeypatch.setattr(scorer, "score_outcome", fake_score_outcome)
    return workspace_root


def test_run_full_no_prompts(monkeypatch):
    with _tmp_path() as tmp_path:
        workspace_root = _patch_full_run(monkeypatch, tmp_path)
        monkeypatch.setattr(
            builtins,
            "input",
            lambda *args, **kwargs: (_ for _ in ()).throw(AssertionError("input() should not be used")),
        )

        class FakeQuestionary:
            @staticmethod
            def confirm(*args, **kwargs):
                raise AssertionError("questionary should not be used")

        monkeypatch.setitem(sys.modules, "questionary", FakeQuestionary)

        args = Args(
            interactive=False,
            suite="refactor-storm",
            agent=None,
            agent_cmd="echo test",
            timeout=10,
            output=str(tmp_path / "result.json"),
            format="json",
            workspace_dir=str(workspace_root),
            full=True,
        )

        assert cli_main.cmd_run_full(args) == 0


def test_run_full_submit_cta_printed(monkeypatch, capsys):
    with _tmp_path() as tmp_path:
        workspace_root = _patch_full_run(monkeypatch, tmp_path)

        args = Args(
            interactive=False,
            suite="refactor-storm",
            agent=None,
            agent_cmd="echo test",
            timeout=10,
            output=str(tmp_path / "result.json"),
            format="json",
            workspace_dir=str(workspace_root),
            full=True,
        )

        assert cli_main.cmd_run_full(args) == 0
        captured = capsys.readouterr()
        assert "\nSubmit: janus-labs submit" in captured.out


def test_run_full_writes_bundles_workspace_hash_and_cli_version(monkeypatch):
    with _tmp_path() as tmp_path:
        suite, workspace_root = _stub_suite(tmp_path, behavior_count=2)
        monkeypatch.setattr(cli_main, "get_suite", lambda suite_id: suite)

        def fake_run_agent_on_workspace(adapter, workspace_dir, prompt, behavior_id):
            return agent_adapter.AgentRunResult(
                behavior_id=behavior_id,
                success=True,
                duration_ms=2500,
                stdout=f"agent output for {behavior_id}",
                stderr="",
                error=None,
            )

        def fake_score_outcome(workspace_dir, behavior_id, threshold, rubric):
            return SimpleNamespace(
                normalized_score=0.82,
                passed_threshold=True,
                git_diff={"patch": f"diff for {behavior_id}", "files_changed": ["src/app.py"]},
                test_results={"output": f"tests for {behavior_id}", "passed": 1, "failed": 0},
                workspace_hash=f"hash-{behavior_id}",
            )

        monkeypatch.setattr(agent_adapter, "run_agent_on_workspace", fake_run_agent_on_workspace)
        monkeypatch.setattr(scorer, "score_outcome", fake_score_outcome)

        output_path = tmp_path / "result.json"
        args = Args(
            interactive=False,
            suite="refactor-storm",
            agent=None,
            agent_cmd="echo test",
            timeout=10,
            output=str(output_path),
            format="json",
            workspace_dir=str(workspace_root),
            full=True,
        )

        assert cli_main.cmd_run_full(args) == 0

        data = json.loads(output_path.read_text(encoding="utf-8"))
        assert len(data["bundles"]) == 2
        assert data["cli_version"] == cli_main.__version__
        expected_hash = hashlib.sha256(
            ":".join(sorted(["hash-BHV-001-test-behavior", "hash-BHV-002-test-behavior"])).encode("utf-8")
        ).hexdigest()[:16]
        assert data["workspace_hash"] == expected_hash

        first_bundle = data["bundles"][0]
        assert first_bundle["behavior_id"] == "BHV-001-test-behavior"
        assert first_bundle["behavior_name"] == "Test behavior 1"
        assert first_bundle["behavior_description"] == "Description 1"
        assert first_bundle["config_fingerprint"]
        assert len(first_bundle["rollouts"]) == 1
        rollout = first_bundle["rollouts"][0]
        assert rollout["prompt"] == "Do the thing for BHV-001-test-behavior"
        assert rollout["response"] == "agent output for BHV-001-test-behavior"
        assert rollout["repo_diff"] == "diff for BHV-001-test-behavior"
        assert rollout["test_output"] == "tests for BHV-001-test-behavior"


def test_run_full_no_ansi(monkeypatch, capsys):
    with _tmp_path() as tmp_path:
        workspace_root = _patch_full_run(monkeypatch, tmp_path)

        args = Args(
            interactive=False,
            suite="refactor-storm",
            agent=None,
            agent_cmd="echo test",
            timeout=10,
            output=str(tmp_path / "result.json"),
            format="json",
            workspace_dir=str(workspace_root),
            full=True,
        )

        assert cli_main.cmd_run_full(args) == 0
        captured = capsys.readouterr()
        assert "\x1b[" not in captured.out


def test_output_per_behavior_format(monkeypatch, capsys):
    with _tmp_path() as tmp_path:
        workspace_root = _patch_full_run(monkeypatch, tmp_path)

        args = Args(
            interactive=False,
            suite="refactor-storm",
            agent=None,
            agent_cmd="echo test",
            timeout=10,
            output=str(tmp_path / "result.json"),
            format="json",
            workspace_dir=str(workspace_root),
            full=True,
        )

        assert cli_main.cmd_run_full(args) == 0
        captured = capsys.readouterr()
        assert "PASS BHV-001" in captured.out


def test_output_summary_with_axes(monkeypatch, capsys):
    with _tmp_path() as tmp_path:
        workspace_root = _patch_full_run(monkeypatch, tmp_path)

        args = Args(
            interactive=False,
            suite="refactor-storm",
            agent=None,
            agent_cmd="echo test",
            timeout=10,
            output=str(tmp_path / "result.json"),
            format="json",
            workspace_dir=str(workspace_root),
            full=True,
        )

        assert cli_main.cmd_run_full(args) == 0
        lines = capsys.readouterr().out.splitlines()
        assert any(
            "Score:" in line and "CQ:" in line and "ER:" in line and "|" in line
            for line in lines
        )


def test_output_no_indent_on_key_lines(monkeypatch, capsys):
    with _tmp_path() as tmp_path:
        workspace_root = _patch_full_run(monkeypatch, tmp_path)

        args = Args(
            interactive=False,
            suite="refactor-storm",
            agent=None,
            agent_cmd="echo test",
            timeout=10,
            output=str(tmp_path / "result.json"),
            format="json",
            workspace_dir=str(workspace_root),
            full=True,
        )

        assert cli_main.cmd_run_full(args) == 0
        lines = capsys.readouterr().out.splitlines()
        prefixes = ("PASS", "FAIL", "Score:", "Submit:", "Results:", "Duration:")
        for line in lines:
            if line.lstrip().startswith(prefixes):
                assert line == line.lstrip()


def test_run_full_interactive_uses_board(monkeypatch):
    with _tmp_path() as tmp_path:
        workspace_root = _patch_full_run(monkeypatch, tmp_path)
        calls = {}

        def fake_init(self, behavior_ids, behavior_names):
            calls["behavior_ids"] = behavior_ids
            calls["behavior_names"] = behavior_names

        monkeypatch.setattr(terminal_ui.ProgressBoard, "__init__", fake_init)
        monkeypatch.setattr(terminal_ui.ProgressBoard, "render", lambda self: None)
        monkeypatch.setattr(terminal_ui.ProgressBoard, "start_behavior", lambda self, behavior_id: None)
        monkeypatch.setattr(terminal_ui.ProgressBoard, "finish_behavior", lambda self, behavior_id, success, duration_ms: None)
        monkeypatch.setattr(interactive_mod, "is_dumb_terminal", lambda: True)
        monkeypatch.setattr(interactive_mod, "simple_confirm", lambda prompt: False)

        args = Args(
            interactive=True,
            suite="refactor-storm",
            agent=None,
            agent_cmd="echo test",
            timeout=10,
            output=str(tmp_path / "result.json"),
            format="json",
            workspace_dir=str(workspace_root),
            full=True,
        )

        assert cli_main.cmd_run_full(args) == 0
        assert calls["behavior_ids"] == ["BHV-001-test-behavior"]
        assert calls["behavior_names"] == ["Test behavior 1"]


def test_interactive_flag_default():
    parser = cli_main.build_parser()
    args = parser.parse_args(["run", "--full", "--suite", "refactor-storm", "--agent", "codex"])
    assert args.interactive is False
