"""Tests for EngineProvisioner with ProgressReporter.

Validates:
- ProgressReporter integration in provisioner
- Customer-facing messages contain no internal names
- on_status callback still works alongside ProgressReporter
"""

from __future__ import annotations

from unittest.mock import patch

import pytest

from llmhosts.discovery.models import GPUInfo, HardwareProfile
from llmhosts.discovery.provisioner import EngineProvisioner
from llmhosts.progress import NullProgressReporter

# Internal names that must NEVER appear in customer-facing messages
_FORBIDDEN_STRINGS = ["ollama", "Ollama", "winget", "qwen", "Qwen"]


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _hw(vram_mb: int = 8192, ram_gb: float = 16.0) -> HardwareProfile:
    gpus = [GPUInfo(name="Test GPU", vram_total_mb=vram_mb)] if vram_mb > 0 else []
    return HardwareProfile(ram_total_gb=ram_gb, gpus=gpus)


# ---------------------------------------------------------------------------
# ProgressReporter integration
# ---------------------------------------------------------------------------


@pytest.mark.unit
class TestProvisionerProgressReporter:
    """Verify provisioner emits ProgressEvents via the reporter."""

    async def test_provision_emits_progress_events(self) -> None:
        """Full provision flow should emit progress events."""
        reporter = NullProgressReporter()
        provisioner = EngineProvisioner(progress=reporter)
        hw = _hw(vram_mb=8192)

        with patch.object(provisioner, "_download_model", return_value=True):
            result = await provisioner.provision(hw)

        assert result.success is True
        assert len(reporter.events) >= 2  # At least pull message + complete
        # All event messages should be customer-friendly
        for event in reporter.events:
            for forbidden in _FORBIDDEN_STRINGS:
                assert forbidden not in event.message, f"Event message contains '{forbidden}': {event.message}"

    async def test_on_status_callback_still_works(self) -> None:
        """Legacy on_status callback should still fire alongside ProgressReporter."""
        status_messages: list[str] = []
        reporter = NullProgressReporter()
        provisioner = EngineProvisioner(
            on_status=status_messages.append,
            progress=reporter,
        )
        hw = _hw(vram_mb=8192)

        with patch.object(provisioner, "_download_model", return_value=True):
            await provisioner.provision(hw)

        # Both should have received messages
        assert len(status_messages) >= 1
        assert len(reporter.events) >= 1


# ---------------------------------------------------------------------------
# Customer-facing message validation
# ---------------------------------------------------------------------------


@pytest.mark.unit
class TestProvisionerMessagesClean:
    """All user-facing messages from the provisioner should be clean."""

    async def test_provision_failure_messages_clean(self) -> None:
        """Failure messages should not contain internal names."""
        reporter = NullProgressReporter()
        provisioner = EngineProvisioner(progress=reporter)
        hw = _hw(vram_mb=8192)

        with patch.object(provisioner, "_download_model", return_value=False):
            result = await provisioner.provision(hw)

        assert result.success is False
        # Check error message
        for forbidden in _FORBIDDEN_STRINGS:
            assert forbidden not in result.error, f"Error message contains '{forbidden}': {result.error}"
        # Check all events
        for event in reporter.events:
            for forbidden in _FORBIDDEN_STRINGS:
                assert forbidden not in event.message, f"Event message contains '{forbidden}': {event.message}"
