"""Tests for the decompose CLI command flags.

Validates that the new codebase-aware decomposition flags (--scan-root,
--min-complexity, --no-validate, --no-merge) are parsed correctly and
that invalid inputs are rejected.
"""

from __future__ import annotations

import subprocess
import sys
import textwrap

import pytest
from click.testing import CliRunner

from loom.cli import cli, decompose_cmd


@pytest.fixture()
def runner():
    """Create a Click CliRunner for invoking CLI commands."""
    return CliRunner()


class TestDecomposeFlags:
    """Tests for the new decompose command flags."""

    def test_help_shows_all_new_flags(self, runner: CliRunner):
        """Verify --help output lists all new flags."""
        result = runner.invoke(cli, ["decompose", "--help"])
        assert result.exit_code == 0
        assert "--scan-root" in result.output
        assert "--min-complexity" in result.output
        assert "--no-validate" in result.output
        assert "--no-merge" in result.output

    def test_help_shows_existing_flags(self, runner: CliRunner):
        """Verify --help still shows the existing flags (backward compat)."""
        result = runner.invoke(cli, ["decompose", "--help"])
        assert result.exit_code == 0
        assert "--from" in result.output
        assert "--depth" in result.output
        assert "--no-enrich" in result.output
        assert "--yes" in result.output

    def test_no_validate_flag(self, runner: CliRunner):
        """--no-validate should be accepted without error (with missing goal)."""
        result = runner.invoke(cli, ["decompose", "--no-validate"])
        # Should fail because no goal is provided, not because of flag parsing
        assert "Provide a goal" in result.output

    def test_no_merge_flag(self, runner: CliRunner):
        """--no-merge should be accepted without error (with missing goal)."""
        result = runner.invoke(cli, ["decompose", "--no-merge"])
        assert "Provide a goal" in result.output

    def test_scan_root_nonexistent_path(self, runner: CliRunner, tmp_path):
        """--scan-root with nonexistent path should produce an error."""
        nonexistent = str(tmp_path / "does_not_exist")
        result = runner.invoke(cli, ["decompose", "--scan-root", nonexistent, "build a thing"])
        assert result.exit_code != 0
        assert "does not exist" in result.output

    def test_scan_root_not_a_directory(self, runner: CliRunner, tmp_path):
        """--scan-root pointing to a file (not directory) should produce an error."""
        a_file = tmp_path / "somefile.txt"
        a_file.write_text("hello")
        result = runner.invoke(cli, ["decompose", "--scan-root", str(a_file), "build a thing"])
        assert result.exit_code != 0
        assert "not a directory" in result.output

    def test_min_complexity_valid(self, runner: CliRunner):
        """--min-complexity with a valid integer >= 1 should be accepted."""
        result = runner.invoke(cli, ["decompose", "--min-complexity", "5"])
        # Should fail because no goal is provided, not because of flag parsing
        assert "Provide a goal" in result.output

    def test_min_complexity_zero_rejected(self, runner: CliRunner):
        """--min-complexity 0 should be rejected (must be >= 1)."""
        result = runner.invoke(cli, ["decompose", "--min-complexity", "0", "build a thing"])
        assert result.exit_code != 0
        assert "must be >= 1" in result.output.lower() or "Invalid" in result.output

    def test_min_complexity_negative_rejected(self, runner: CliRunner):
        """--min-complexity -1 should be rejected (must be >= 1)."""
        result = runner.invoke(cli, ["decompose", "--min-complexity", "-1", "build a thing"])
        assert result.exit_code != 0
        assert "must be >= 1" in result.output.lower() or "Invalid" in result.output

    def test_min_complexity_non_integer_rejected(self, runner: CliRunner):
        """--min-complexity with a non-integer should be rejected."""
        result = runner.invoke(cli, ["decompose", "--min-complexity", "abc", "build a thing"])
        assert result.exit_code != 0

    def test_all_flags_together(self, runner: CliRunner, tmp_path):
        """All new flags can be combined together."""
        result = runner.invoke(cli, [
            "decompose",
            "--scan-root", str(tmp_path),
            "--min-complexity", "5",
            "--no-validate",
            "--no-merge",
        ])
        # Should fail because no goal is provided, not because of flag parsing
        assert "Provide a goal" in result.output


class TestDecomposeSubprocess:
    """Test decompose --help via subprocess to verify end-to-end CLI."""

    def test_subprocess_help_shows_new_flags(self):
        """Run `python -m loom decompose --help` as a subprocess and check output."""
        result = subprocess.run(
            [sys.executable, "-m", "loom", "decompose", "--help"],
            capture_output=True,
            text=True,
            timeout=30,
        )
        assert result.returncode == 0
        assert "--scan-root" in result.stdout
        assert "--min-complexity" in result.stdout
        assert "--no-validate" in result.stdout
        assert "--no-merge" in result.stdout
        # Verify help descriptions
        assert "Scan project directory" in result.stdout
        assert "Minimum complexity threshold" in result.stdout
        assert "Skip codebase validation" in result.stdout
        assert "Skip merging of trivial" in result.stdout
