"""Go2rtc test configuration."""

from collections.abc import Generator
from pathlib import Path
from unittest.mock import AsyncMock, Mock, patch

from awesomeversion import AwesomeVersion
from go2rtc_client.rest import _SchemesClient, _StreamClient, _WebRTCClient
import pytest

from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
from homeassistant.components.go2rtc.const import DOMAIN, RECOMMENDED_VERSION
from homeassistant.components.go2rtc.server import Server
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component

from . import MockCamera

from tests.common import (
    MockConfigEntry,
    MockModule,
    mock_config_flow,
    mock_integration,
    mock_platform,
    setup_test_component_platform,
)

GO2RTC_PATH = "homeassistant.components.go2rtc"


@pytest.fixture
def rest_client() -> Generator[AsyncMock]:
    """Mock a go2rtc rest client."""
    with (
        patch(
            "homeassistant.components.go2rtc.Go2RtcRestClient", autospec=True
        ) as mock_client,
        patch("homeassistant.components.go2rtc.server.Go2RtcRestClient", mock_client),
    ):
        client = mock_client.return_value
        client.schemes = schemes = Mock(spec_set=_SchemesClient)
        schemes.list.return_value = {
            "onvif",
            "exec",
            "http",
            "rtmps",
            "https",
            "rtmpx",
            "httpx",
            "rtsps",
            "webrtc",
            "rtmp",
            "tcp",
            "rtsp",
            "rtspx",
            "ffmpeg",
        }
        client.streams = streams = Mock(spec_set=_StreamClient)
        streams.list.return_value = {}
        client.validate_server_version = AsyncMock(
            return_value=AwesomeVersion(RECOMMENDED_VERSION)
        )
        client.webrtc = Mock(spec_set=_WebRTCClient)
        yield client


@pytest.fixture
def ws_client() -> Generator[Mock]:
    """Mock a go2rtc websocket client."""
    with patch(
        "homeassistant.components.go2rtc.Go2RtcWsClient", autospec=True
    ) as ws_client_mock:
        yield ws_client_mock.return_value


@pytest.fixture
def server_stdout() -> list[str]:
    """Server stdout lines."""
    return [
        "09:00:03.466 INF go2rtc platform=linux/amd64 revision=780f378 version=1.9.5",
        "09:00:03.466 INF config path=/tmp/go2rtc.yaml",
        "09:00:03.467 INF [rtsp] listen addr=:8554",
        "09:00:03.467 INF [api] listen addr=127.0.0.1:1984",
        "09:00:03.467 INF [webrtc] listen addr=:8555/tcp",
    ]


@pytest.fixture
def mock_create_subprocess(server_stdout: list[str]) -> Generator[AsyncMock]:
    """Mock create_subprocess_exec."""
    with patch(f"{GO2RTC_PATH}.server.asyncio.create_subprocess_exec") as mock_subproc:
        subproc = AsyncMock()
        subproc.terminate = Mock()
        subproc.kill = Mock()
        subproc.returncode = None
        # Simulate process output
        subproc.stdout.__aiter__.return_value = iter(
            [f"{entry}\n".encode() for entry in server_stdout]
        )
        mock_subproc.return_value = subproc
        yield mock_subproc


@pytest.fixture
def server_start(mock_create_subprocess: AsyncMock) -> Generator[AsyncMock]:
    """Mock start of a go2rtc server."""
    with patch(
        f"{GO2RTC_PATH}.server.Server.start", wraps=Server.start, autospec=True
    ) as mock_server_start:
        yield mock_server_start


@pytest.fixture
def server_stop() -> Generator[AsyncMock]:
    """Mock stop of a go2rtc server."""
    with (
        patch(
            f"{GO2RTC_PATH}.server.Server.stop", wraps=Server.stop, autospec=True
        ) as mock_server_stop,
    ):
        yield mock_server_stop


@pytest.fixture
def server(server_start: AsyncMock, server_stop: AsyncMock) -> Generator[AsyncMock]:
    """Mock a go2rtc server."""
    with patch(f"{GO2RTC_PATH}.Server", wraps=Server) as mock_server:
        yield mock_server


@pytest.fixture(name="is_docker_env")
def is_docker_env_fixture() -> bool:
    """Fixture to provide is_docker_env return value."""
    return True


@pytest.fixture
def mock_is_docker_env(is_docker_env: bool) -> Generator[Mock]:
    """Mock is_docker_env."""
    with patch(
        "homeassistant.components.go2rtc.is_docker_env",
        return_value=is_docker_env,
    ) as mock_is_docker_env:
        yield mock_is_docker_env


@pytest.fixture(name="go2rtc_binary")
def go2rtc_binary_fixture() -> str:
    """Fixture to provide go2rtc binary name."""
    return "/usr/bin/go2rtc"


@pytest.fixture
def mock_get_binary(go2rtc_binary: str) -> Generator[Mock]:
    """Mock _get_binary."""
    with patch(
        "homeassistant.components.go2rtc.shutil.which",
        return_value=go2rtc_binary,
    ) as mock_which:
        yield mock_which


@pytest.fixture
async def init_integration(
    hass: HomeAssistant,
    rest_client: AsyncMock,
    mock_is_docker_env: Generator[Mock],
    mock_get_binary: Generator[Mock],
    server: Mock,
) -> None:
    """Initialize the go2rtc integration."""
    assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})


TEST_DOMAIN = "test"


@pytest.fixture
def integration_config_entry(hass: HomeAssistant) -> ConfigEntry:
    """Test mock config entry."""
    entry = MockConfigEntry(domain=TEST_DOMAIN)
    entry.add_to_hass(hass)
    return entry


@pytest.fixture
def camera_unique_id() -> str | None:
    """Camera unique ID."""
    return "camera_unique_id"


@pytest.fixture
async def init_test_integration(
    hass: HomeAssistant,
    integration_config_entry: ConfigEntry,
    camera_unique_id: str | None,
) -> MockCamera:
    """Initialize components."""

    async def async_setup_entry_init(
        hass: HomeAssistant, config_entry: ConfigEntry
    ) -> bool:
        """Set up test config entry."""
        await hass.config_entries.async_forward_entry_setups(
            config_entry, [Platform.CAMERA]
        )
        return True

    async def async_unload_entry_init(
        hass: HomeAssistant, config_entry: ConfigEntry
    ) -> bool:
        """Unload test config entry."""
        await hass.config_entries.async_forward_entry_unload(
            config_entry, Platform.CAMERA
        )
        return True

    mock_integration(
        hass,
        MockModule(
            TEST_DOMAIN,
            async_setup_entry=async_setup_entry_init,
            async_unload_entry=async_unload_entry_init,
        ),
    )
    test_camera = MockCamera(camera_unique_id)
    setup_test_component_platform(
        hass, CAMERA_DOMAIN, [test_camera], from_config_entry=True
    )
    mock_platform(hass, f"{TEST_DOMAIN}.config_flow", Mock())

    with mock_config_flow(TEST_DOMAIN, ConfigFlow):
        assert await hass.config_entries.async_setup(integration_config_entry.entry_id)
        await hass.async_block_till_done()

    return test_camera


@pytest.fixture
def server_dir(tmp_path: Path) -> Generator[Path]:
    """Fixture to provide a temporary directory for the server."""
    server_dir = tmp_path / "go2rtc"
    server_dir.mkdir()
    with patch(
        "homeassistant.components.go2rtc.mkdtemp",
        return_value=str(server_dir),
    ):
        yield server_dir
