from unittest.mock import patch, MagicMock
from pathlib import Path

import pytest
from click.testing import CliRunner
from onako.cli import main


@pytest.fixture(autouse=True)
def isolate_from_live_server(tmp_path):
    """Prevent tests from touching the real PID file, port, or processes."""
    fake_pid = tmp_path / "onako.pid"
    fake_dev = tmp_path / "onako.dev"
    with patch("onako.cli.PID_FILE", fake_pid), \
         patch("onako.cli.DEV_MARKER", fake_dev), \
         patch("onako.cli._find_server_pid", return_value=None):
        yield fake_pid


def test_status_not_running():
    """Status reports not running when server is unreachable."""
    runner = CliRunner()
    with patch("urllib.request.urlopen", side_effect=Exception("refused")):
        result = runner.invoke(main, ["status"])
    assert result.exit_code == 0
    assert "not running" in result.output


def test_status_running():
    """Status reports running when health endpoint responds."""
    mock_response = MagicMock()
    mock_response.read.return_value = b'{"status": "ok"}'
    runner = CliRunner()
    with patch("urllib.request.urlopen", return_value=mock_response):
        result = runner.invoke(main, ["status"])
    assert result.exit_code == 0
    assert "running" in result.output


def test_stop_no_server():
    """Stop reports not running when no PID file and no server on port."""
    runner = CliRunner()
    result = runner.invoke(main, ["stop"])
    assert result.exit_code == 0
    assert "not running" in result.output


def test_stop_with_pid_file(tmp_path, isolate_from_live_server):
    """Stop kills the process from the PID file."""
    pid_file = isolate_from_live_server
    pid_file.write_text("12345")

    with patch("onako.cli.os.kill") as mock_kill:
        # First call (signal 0) = check exists, second (signal 15) = SIGTERM,
        # third (signal 0) = check dead → ProcessLookupError
        mock_kill.side_effect = [None, None, ProcessLookupError]
        runner = CliRunner()
        result = runner.invoke(main, ["stop"])

    assert result.exit_code == 0
    assert "stopped" in result.output.lower()
    assert "12345" in result.output
    assert not pid_file.exists()


def test_stop_stale_pid_file(isolate_from_live_server):
    """Stop cleans up stale PID file when process doesn't exist."""
    pid_file = isolate_from_live_server
    pid_file.write_text("99999")

    with patch("onako.cli.os.kill", side_effect=ProcessLookupError):
        runner = CliRunner()
        result = runner.invoke(main, ["stop"])

    assert result.exit_code == 0
    assert "not running" in result.output


def test_stop_falls_back_to_port():
    """Stop finds server by port when PID file is missing."""
    runner = CliRunner()
    with patch("onako.cli._find_server_pid", return_value=54321), \
         patch("onako.cli.os.kill") as mock_kill:
        mock_kill.side_effect = [None, ProcessLookupError]  # SIGTERM, then dead
        result = runner.invoke(main, ["stop"])

    assert result.exit_code == 0
    assert "stopped" in result.output.lower()
    assert "54321" in result.output
