"""Fixtures for harmony tests."""
from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch

from aioharmony.const import ClientCallbackType
import pytest

from homeassistant.components.harmony.const import ACTIVITY_POWER_OFF

from .const import NILE_TV_ACTIVITY_ID, PLAY_MUSIC_ACTIVITY_ID, WATCH_TV_ACTIVITY_ID

ACTIVITIES_TO_IDS = {
    ACTIVITY_POWER_OFF: -1,
    "Watch TV": WATCH_TV_ACTIVITY_ID,
    "Play Music": PLAY_MUSIC_ACTIVITY_ID,
    "Nile-TV": NILE_TV_ACTIVITY_ID,
}

IDS_TO_ACTIVITIES = {
    -1: ACTIVITY_POWER_OFF,
    WATCH_TV_ACTIVITY_ID: "Watch TV",
    PLAY_MUSIC_ACTIVITY_ID: "Play Music",
    NILE_TV_ACTIVITY_ID: "Nile-TV",
}

TV_DEVICE_ID = 1234
TV_DEVICE_NAME = "My TV"

DEVICES_TO_IDS = {
    TV_DEVICE_NAME: TV_DEVICE_ID,
}

IDS_TO_DEVICES = {
    TV_DEVICE_ID: TV_DEVICE_NAME,
}


class FakeHarmonyClient:
    """FakeHarmonyClient to mock away network calls."""

    def initialize(
        self, ip_address: str = "", callbacks: ClientCallbackType = MagicMock()
    ):
        """Initialize FakeHarmonyClient class to capture callbacks."""
        self._activity_name = "Watch TV"
        self.close = AsyncMock()
        self.send_commands = AsyncMock()
        self.change_channel = AsyncMock()
        self.sync = AsyncMock()
        self._callbacks = callbacks
        self.fw_version = "123.456"

        return self

    async def connect(self):
        """Connect and call the appropriate callbacks."""
        self._callbacks.connect(None)
        return AsyncMock(return_value=(True))

    def get_activity_name(self, activity_id):
        """Return the activity name with the given activity_id."""
        return IDS_TO_ACTIVITIES.get(activity_id)

    def get_activity_id(self, activity_name):
        """Return the mapping of an activity name to the internal id."""
        return ACTIVITIES_TO_IDS.get(activity_name)

    def get_device_name(self, device_id):
        """Return the device name with the given device_id."""
        return IDS_TO_DEVICES.get(device_id)

    def get_device_id(self, device_name):
        """Return the device id with the given device_name."""
        return DEVICES_TO_IDS.get(device_name)

    async def start_activity(self, activity_id):
        """Update the current activity and call the appropriate callbacks."""
        self._activity_name = IDS_TO_ACTIVITIES.get(int(activity_id))
        activity_tuple = (activity_id, self._activity_name)
        self._callbacks.new_activity_starting(activity_tuple)
        self._callbacks.new_activity(activity_tuple)

        return AsyncMock(return_value=(True, "unused message"))

    async def power_off(self):
        """Power off all activities."""
        await self.start_activity(-1)

    @property
    def current_activity(self):
        """Return the current activity tuple."""
        return (
            self.get_activity_id(self._activity_name),
            self._activity_name,
        )

    @property
    def config(self):
        """Return the config object."""
        return self.hub_config.config

    @property
    def json_config(self):
        """Return the json config as a dict."""
        return {}

    @property
    def hub_config(self):
        """Return the client_config type."""
        config = MagicMock()
        type(config).activities = PropertyMock(
            return_value=[
                {"name": "Watch TV", "id": WATCH_TV_ACTIVITY_ID},
                {"name": "Play Music", "id": PLAY_MUSIC_ACTIVITY_ID},
                {"name": "Nile-TV", "id": NILE_TV_ACTIVITY_ID},
            ]
        )
        type(config).devices = PropertyMock(
            return_value=[{"name": TV_DEVICE_NAME, "id": TV_DEVICE_ID}]
        )
        type(config).info = PropertyMock(return_value={})
        type(config).hub_state = PropertyMock(return_value={})
        type(config).config = PropertyMock(
            return_value={
                "activity": [
                    {"id": 10000, "label": None},
                    {"id": -1, "label": "PowerOff"},
                    {"id": WATCH_TV_ACTIVITY_ID, "label": "Watch TV"},
                    {"id": PLAY_MUSIC_ACTIVITY_ID, "label": "Play Music"},
                    {"id": NILE_TV_ACTIVITY_ID, "label": "Nile-TV"},
                ]
            }
        )
        return config

    def mock_reconnection(self):
        """Simulate reconnection to the hub."""
        self._callbacks.connect(None)

    def mock_disconnection(self):
        """Simulate disconnection to the hub."""
        self._callbacks.disconnect(None)


@pytest.fixture()
def harmony_client():
    """Create the FakeHarmonyClient instance."""
    return FakeHarmonyClient()


@pytest.fixture()
def mock_hc(harmony_client):
    """Patch the real HarmonyClient with initialization side effect."""

    with patch(
        "homeassistant.components.harmony.data.HarmonyClient",
        side_effect=harmony_client.initialize,
    ) as fake:
        yield fake


@pytest.fixture()
def mock_write_config():
    """Patches write_config_file to remove side effects."""
    with patch(
        "homeassistant.components.harmony.remote.HarmonyRemote.write_config_file",
    ) as mock:
        yield mock
