"""Tests verifying LLMHosts discovery only makes read-only operations.

The scanner now reads local GGUF files (filesystem-only, no HTTP).
These tests verify that discovery never modifies files, engines, or
system state.
"""

from __future__ import annotations

import os
from unittest.mock import AsyncMock, patch


class TestDiscoveryReadOnly:
    """Verify that discovery operations are purely read-only."""

    async def test_scanner_only_reads_filesystem(self):
        """UniversalScanner should only list local models (no HTTP, no writes)."""
        from llmhosts.discovery.scanner import UniversalScanner

        scanner = UniversalScanner()

        # Scanner should only call list_local_models
        with patch("llmhosts.models.download.list_local_models", return_value=["qwen3:8b"]) as mock_list:
            engines = await scanner.scan()

        mock_list.assert_called_once()
        assert len(engines) == 1

    def test_provision_notice_exists(self):
        """Server should have a notice method describing auto-provision behavior."""
        from llmhosts.server import LLMHostsServer

        notice = LLMHostsServer.read_only_notice()
        assert "auto-provision" in notice.lower()

    async def test_hardware_detection_is_read_only(self):
        """Hardware detection should only read system info, not modify it."""
        import contextlib

        with patch("asyncio.create_subprocess_exec") as mock_exec:
            mock_proc = AsyncMock()
            mock_proc.communicate.return_value = (
                b"GPU 0: NVIDIA GeForce RTX 4090",
                b"",
            )
            mock_proc.returncode = 0
            mock_exec.return_value = mock_proc

            with contextlib.suppress(Exception):
                from llmhosts.discovery.hardware import HardwareDetector

                await HardwareDetector.detect()

            # Verify no destructive commands were run
            for call in mock_exec.call_args_list:
                cmd_parts = call[0] if call[0] else call.kwargs.get("args", [])
                cmd_str = " ".join(str(p) for p in cmd_parts)
                assert not any(
                    dangerous in cmd_str.lower()
                    for dangerous in ["rm ", "kill", "stop", "restart", "install", "uninstall", "write", "modify"]
                ), f"Hardware detection ran potentially destructive command: {cmd_str}"

    def test_discovery_endpoints_are_documented(self):
        """Verify key discovery docs exist."""
        doc_path = os.path.join(os.path.dirname(__file__), "..", "docs", "guides", "read-only-guarantee.md")
        assert os.path.exists(doc_path), "read-only-guarantee.md documentation missing"

    def test_dry_run_flag_exists_on_serve(self):
        """The serve command should accept --dry-run."""
        from click.testing import CliRunner

        from llmhosts.cli import main

        runner = CliRunner()
        result = runner.invoke(main, ["serve", "--help"])
        assert "--dry-run" in result.output, "serve command missing --dry-run flag"

    def test_dry_run_flag_exists_on_up(self):
        """The up command should accept --dry-run."""
        from click.testing import CliRunner

        from llmhosts.cli import main

        runner = CliRunner()
        result = runner.invoke(main, ["up", "--help"])
        assert "--dry-run" in result.output, "up command missing --dry-run flag"
