from pathlib import Path
from unittest.mock import patch

from armorer.main import (
    _auto_remap_conflicting_ports,
    _containers_using_port,
    _detect_stack_instability,
    _extract_port_conflict,
    _is_expected_zero_exit,
)


def test_extract_port_conflict_parses_bind_error():
    err = "Error response from daemon: Bind for 0.0.0.0:18789 failed: port is already allocated"
    assert _extract_port_conflict(err) == 18789


def test_extract_port_conflict_none_for_unrelated_error():
    assert _extract_port_conflict("some other compose error") is None


@patch("armorer.main.subprocess.run")
def test_containers_using_port_parses_names(mock_run):
    class _Res:
        stdout = "a\nb\n"
    mock_run.return_value = _Res()
    assert _containers_using_port(1234) == ["a", "b"]


@patch("armorer.main._docker_published_ports")
def test_auto_remap_conflicting_ports_updates_env_var(mock_ports, tmp_path: Path):
    mock_ports.return_value = {18789}
    install_dir = tmp_path / "app"
    install_dir.mkdir(parents=True, exist_ok=True)
    (install_dir / ".env").write_text("OPENCLAW_GATEWAY_PORT=18789\n")
    (install_dir / "docker-compose.yml").write_text(
        """
services:
  gateway:
    image: test
    ports:
      - "${OPENCLAW_GATEWAY_PORT:-18789}:18789"
""".strip()
        + "\n"
    )

    remaps = _auto_remap_conflicting_ports(install_dir, target_port=18789)
    assert 18789 in remaps
    updated_env = (install_dir / ".env").read_text()
    assert f"OPENCLAW_GATEWAY_PORT={remaps[18789]}" in updated_env


def test_is_expected_zero_exit_allows_deps_jobs():
    assert _is_expected_zero_exit("deps", 0) is True
    assert _is_expected_zero_exit("deps_backend", 0) is True


@patch("armorer.main._parse_compose_ps_entries")
def test_detect_stack_instability_ignores_exited_deps(mock_ps, tmp_path: Path):
    mock_ps.return_value = [
        {"Service": "deps", "State": "exited", "Status": "Exited (0)", "ExitCode": 0},
        {"Service": "deps_backend", "State": "exited", "Status": "Exited (0)", "ExitCode": 0},
        {"Service": "redis", "State": "running", "Status": "Up", "Health": "healthy", "ExitCode": 0},
    ]
    assert _detect_stack_instability(tmp_path) is None
