"""Tests for CLI argument parsing and commands."""

from __future__ import annotations

import json
import subprocess
import sys
from unittest.mock import MagicMock

import pytest

from cleave import __version__
from cleave.cli import _format_run_result, _format_architect_result


def test_version_flag_long():
    """Test that --version flag outputs correct version."""
    result = subprocess.run(
        [sys.executable, "-m", "cleave", "--version"],
        capture_output=True,
        text=True,
    )

    assert result.returncode == 0
    assert f"cleave {__version__}" in result.stdout


def test_version_flag_short():
    """Test that -v flag outputs correct version."""
    result = subprocess.run(
        [sys.executable, "-m", "cleave", "-v"],
        capture_output=True,
        text=True,
    )

    assert result.returncode == 0
    assert f"cleave {__version__}" in result.stdout


def test_no_command_shows_error():
    """Test that running cleave without a command shows an error."""
    result = subprocess.run(
        [sys.executable, "-m", "cleave"],
        capture_output=True,
        text=True,
    )

    assert result.returncode == 2
    assert "the following arguments are required: command" in result.stderr


def test_help_flag():
    """Test that --help flag shows usage information."""
    result = subprocess.run(
        [sys.executable, "-m", "cleave", "--help"],
        capture_output=True,
        text=True,
    )

    assert result.returncode == 0
    assert "Cleave CLI - Task decomposition automation" in result.stdout
    assert "Examples:" in result.stdout


# --- JSON format tests ---


class TestFormatRunResult:
    """Tests for _format_run_result() JSON serializer."""

    def _make_result(self, phase="complete", success=True, error=None,
                     workspace_path=None, report_path=None, children=None):
        result = MagicMock()
        result.success = success
        result.error = error
        result.workspace_path = workspace_path
        result.report_path = report_path
        result.state = MagicMock()
        result.state.phase = phase
        result.state.total_cost_usd = 1.23
        result.state.children = children or []
        return result

    def test_success_complete(self):
        result = self._make_result()
        output = _format_run_result(result)
        assert output["success"] is True
        assert output["paused"] is False
        assert output["phase"] == "complete"
        assert output["error"] is None
        assert output["total_cost_usd"] == 1.23

    def test_paused_with_workspace(self, tmp_path):
        plan_review = tmp_path / "plan-review.md"
        plan_review.write_text("# Plan Review")
        result = self._make_result(phase="planned", workspace_path=tmp_path)
        output = _format_run_result(result)
        assert output["paused"] is True
        assert output["plan_review_path"] == str(plan_review)
        assert output["resume_command"] == f"cleave run --resume {tmp_path}"

    def test_paused_no_plan_review(self, tmp_path):
        result = self._make_result(phase="planned", workspace_path=tmp_path)
        output = _format_run_result(result)
        assert output["paused"] is True
        assert output["plan_review_path"] is None
        assert output["resume_command"] is not None

    def test_children_counting(self):
        c1 = MagicMock()
        c1.status = "completed"
        c2 = MagicMock()
        c2.status = "running"
        c3 = MagicMock()
        c3.status = "completed"
        result = self._make_result(children=[c1, c2, c3])
        output = _format_run_result(result)
        assert output["children_completed"] == 2
        assert output["children_total"] == 3

    def test_error_field(self):
        result = self._make_result(success=False, error="budget exhausted")
        output = _format_run_result(result)
        assert output["success"] is False
        assert output["error"] == "budget exhausted"

    def test_output_is_json_serializable(self, tmp_path):
        result = self._make_result(
            workspace_path=tmp_path,
            report_path=tmp_path / "report.md",
        )
        output = _format_run_result(result)
        serialized = json.dumps(output)
        parsed = json.loads(serialized)
        assert parsed["workspace_path"] == str(tmp_path)

    def test_expected_keys(self):
        result = self._make_result()
        output = _format_run_result(result)
        expected_keys = {
            "success", "paused", "phase", "workspace_path", "report_path",
            "plan_review_path", "resume_command", "total_cost_usd",
            "children_completed", "children_total", "error",
        }
        assert set(output.keys()) == expected_keys


class TestFormatArchitectResult:
    """Tests for _format_architect_result() JSON serializer."""

    def test_success_complete(self, tmp_path):
        from cleave.architect.runner import ArchitectResult
        result = ArchitectResult(
            success=True,
            db_path=tmp_path / "architect.db",
            phases_completed=3,
            phases_total=3,
        )
        output = _format_architect_result(result)
        assert output["success"] is True
        assert output["paused"] is False
        assert output["phases_completed"] == 3
        assert output["phases_total"] == 3

    def test_paused_with_plan_review(self, tmp_path):
        from cleave.architect.runner import ArchitectResult
        db_path = tmp_path / "architect.db"
        plan_review = tmp_path / "architect-plan-review.md"
        plan_review.write_text("# Architect Plan")
        result = ArchitectResult(
            success=True,
            db_path=db_path,
            paused=True,
            phases_completed=0,
            phases_total=5,
        )
        output = _format_architect_result(result)
        assert output["paused"] is True
        assert output["plan_review_path"] == str(plan_review)
        assert output["resume_command"] == f"cleave architect --resume {db_path}"

    def test_paused_no_plan_review(self, tmp_path):
        from cleave.architect.runner import ArchitectResult
        result = ArchitectResult(
            success=True,
            db_path=tmp_path / "architect.db",
            paused=True,
        )
        output = _format_architect_result(result)
        assert output["paused"] is True
        assert output["plan_review_path"] is None
        assert output["resume_command"] is not None

    def test_error_field(self, tmp_path):
        from cleave.architect.runner import ArchitectResult
        result = ArchitectResult(
            success=False,
            db_path=tmp_path / "architect.db",
            error="circuit breaker tripped",
        )
        output = _format_architect_result(result)
        assert output["success"] is False
        assert output["error"] == "circuit breaker tripped"

    def test_output_is_json_serializable(self, tmp_path):
        from cleave.architect.runner import ArchitectResult
        result = ArchitectResult(
            success=True,
            db_path=tmp_path / "architect.db",
            report_path=tmp_path / "report.md",
            total_cost_usd=12.34,
        )
        output = _format_architect_result(result)
        serialized = json.dumps(output)
        parsed = json.loads(serialized)
        assert parsed["total_cost_usd"] == 12.34

    def test_expected_keys(self, tmp_path):
        from cleave.architect.runner import ArchitectResult
        result = ArchitectResult(
            success=True,
            db_path=tmp_path / "architect.db",
        )
        output = _format_architect_result(result)
        expected_keys = {
            "success", "paused", "db_path", "report_path",
            "plan_review_path", "resume_command", "total_cost_usd",
            "phases_completed", "phases_total", "error",
        }
        assert set(output.keys()) == expected_keys


class TestRunJsonFlag:
    """Tests that --format json flag produces structured output."""

    def test_run_format_json_produces_valid_json(self, tmp_path):
        """--format json produces valid JSON with expected keys (even on error)."""
        (tmp_path / ".git").mkdir()
        result = subprocess.run(
            [
                sys.executable, "-m", "cleave", "run",
                "-d", "test directive",
                "-r", str(tmp_path),
                "--dry-run", "-f", "json",
            ],
            capture_output=True,
            text=True,
        )
        # May fail (no real git HEAD), but output must be valid JSON
        output = json.loads(result.stdout)
        assert "success" in output
        assert "paused" in output
        assert "phase" in output
        assert "error" in output
        assert "total_cost_usd" in output
        assert "children_completed" in output
        assert "children_total" in output

    def test_run_format_text_no_json_on_error(self, tmp_path):
        """--format text does not emit JSON to stdout."""
        (tmp_path / ".git").mkdir()
        result = subprocess.run(
            [
                sys.executable, "-m", "cleave", "run",
                "-d", "test directive",
                "-r", str(tmp_path),
                "--dry-run", "-f", "text",
            ],
            capture_output=True,
            text=True,
        )
        # Text mode errors go to stderr, stdout should not be parseable JSON
        if result.stdout.strip():
            with pytest.raises(json.JSONDecodeError):
                json.loads(result.stdout)

    def test_run_format_flag_accepted(self):
        """--format flag is accepted by run subparser (help does not error)."""
        result = subprocess.run(
            [sys.executable, "-m", "cleave", "run", "--help"],
            capture_output=True,
            text=True,
        )
        assert result.returncode == 0
        assert "--format" in result.stdout
        assert "{text,json}" in result.stdout
