"""Test Blue Current Init Component."""

from datetime import timedelta
from unittest.mock import patch

from bluecurrent_api.client import Client
from bluecurrent_api.exceptions import (
    BlueCurrentException,
    InvalidApiToken,
    RequestLimitReached,
    WebsocketError,
)
import pytest

from homeassistant.components.blue_current import DOMAIN, Connector, async_setup_entry
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import (
    ConfigEntryAuthFailed,
    ConfigEntryNotReady,
    IntegrationError,
)

from . import init_integration

from tests.common import MockConfigEntry


async def test_load_unload_entry(hass: HomeAssistant) -> None:
    """Test load and unload entry."""
    config_entry = await init_integration(hass, "sensor", {})
    assert config_entry.state == ConfigEntryState.LOADED
    assert isinstance(hass.data[DOMAIN][config_entry.entry_id], Connector)

    await hass.config_entries.async_unload(config_entry.entry_id)
    await hass.async_block_till_done()
    assert config_entry.state == ConfigEntryState.NOT_LOADED
    assert hass.data[DOMAIN] == {}


@pytest.mark.parametrize(
    ("api_error", "config_error"),
    [
        (InvalidApiToken, ConfigEntryAuthFailed),
        (BlueCurrentException, ConfigEntryNotReady),
    ],
)
async def test_config_exceptions(
    hass: HomeAssistant, api_error: BlueCurrentException, config_error: IntegrationError
) -> None:
    """Tests if the correct config error is raised when connecting to the api fails."""
    with patch(
        "homeassistant.components.blue_current.Client.connect",
        side_effect=api_error,
    ), pytest.raises(config_error):
        config_entry = MockConfigEntry(
            domain=DOMAIN,
            entry_id="uuid",
            unique_id="uuid",
            data={"api_token": "123", "card": {"123"}},
        )
        config_entry.add_to_hass(hass)

        await async_setup_entry(hass, config_entry)


async def test_on_data(hass: HomeAssistant) -> None:
    """Test on_data."""

    await init_integration(hass, "sensor", {})

    with patch(
        "homeassistant.components.blue_current.async_dispatcher_send"
    ) as test_async_dispatcher_send:
        connector: Connector = hass.data[DOMAIN]["uuid"]

        # test CHARGE_POINTS
        data = {
            "object": "CHARGE_POINTS",
            "data": [{"evse_id": "101", "model_type": "hidden", "name": ""}],
        }
        await connector.on_data(data)
        assert connector.charge_points == {"101": {"model_type": "hidden", "name": ""}}

        # test CH_STATUS
        data2 = {
            "object": "CH_STATUS",
            "data": {
                "actual_v1": 12,
                "actual_v2": 14,
                "actual_v3": 15,
                "actual_p1": 12,
                "actual_p2": 14,
                "actual_p3": 15,
                "activity": "charging",
                "start_datetime": "2021-11-18T14:12:23",
                "stop_datetime": "2021-11-18T14:32:23",
                "offline_since": "2021-11-18T14:32:23",
                "total_cost": 10.52,
                "vehicle_status": "standby",
                "actual_kwh": 10,
                "evse_id": "101",
            },
        }
        await connector.on_data(data2)
        assert connector.charge_points == {
            "101": {
                "model_type": "hidden",
                "name": "",
                "actual_v1": 12,
                "actual_v2": 14,
                "actual_v3": 15,
                "actual_p1": 12,
                "actual_p2": 14,
                "actual_p3": 15,
                "activity": "charging",
                "start_datetime": "2021-11-18T14:12:23",
                "stop_datetime": "2021-11-18T14:32:23",
                "offline_since": "2021-11-18T14:32:23",
                "total_cost": 10.52,
                "vehicle_status": "standby",
                "actual_kwh": 10,
            }
        }

        test_async_dispatcher_send.assert_called_with(
            hass, "blue_current_value_update_101"
        )

        # test GRID_STATUS
        data3 = {
            "object": "GRID_STATUS",
            "data": {
                "grid_actual_p1": 12,
                "grid_actual_p2": 14,
                "grid_actual_p3": 15,
            },
        }
        await connector.on_data(data3)
        assert connector.grid == {
            "grid_actual_p1": 12,
            "grid_actual_p2": 14,
            "grid_actual_p3": 15,
        }
        test_async_dispatcher_send.assert_called_with(hass, "blue_current_grid_update")


async def test_start_loop(hass: HomeAssistant) -> None:
    """Tests start_loop."""

    with patch(
        "homeassistant.components.blue_current.async_call_later"
    ) as test_async_call_later:
        config_entry = MockConfigEntry(
            domain=DOMAIN,
            entry_id="uuid",
            unique_id="uuid",
            data={"api_token": "123", "card": {"123"}},
        )

        connector = Connector(hass, config_entry, Client)

        with patch(
            "homeassistant.components.blue_current.Client.start_loop",
            side_effect=WebsocketError("unknown command"),
        ):
            await connector.start_loop()
            test_async_call_later.assert_called_with(hass, 1, connector.reconnect)

        with patch(
            "homeassistant.components.blue_current.Client.start_loop",
            side_effect=RequestLimitReached,
        ):
            await connector.start_loop()
            test_async_call_later.assert_called_with(hass, 1, connector.reconnect)


async def test_reconnect(hass: HomeAssistant) -> None:
    """Tests reconnect."""

    with patch(
        "homeassistant.components.blue_current.async_call_later"
    ) as test_async_call_later:
        config_entry = MockConfigEntry(
            domain=DOMAIN,
            entry_id="uuid",
            unique_id="uuid",
            data={"api_token": "123", "card": {"123"}},
        )

        connector = Connector(hass, config_entry, Client)

        with patch(
            "homeassistant.components.blue_current.Client.connect",
            side_effect=WebsocketError,
        ):
            await connector.reconnect()

        test_async_call_later.assert_called_with(hass, 20, connector.reconnect)

        with patch(
            "homeassistant.components.blue_current.Client.connect",
            side_effect=RequestLimitReached,
        ), patch(
            "homeassistant.components.blue_current.Client.get_next_reset_delta",
            return_value=timedelta(hours=1),
        ):
            await connector.reconnect()

        test_async_call_later.assert_called_with(
            hass, timedelta(hours=1), connector.reconnect
        )

        with patch("homeassistant.components.blue_current.Client.connect"), patch(
            "homeassistant.components.blue_current.Connector.start_loop"
        ) as test_start_loop, patch(
            "homeassistant.components.blue_current.Client.get_charge_points"
        ) as test_get_charge_points:
            await connector.reconnect()
            test_start_loop.assert_called_once()
            test_get_charge_points.assert_called_once()