"""Test fixtures for rainbird."""

from __future__ import annotations

from http import HTTPStatus
import json
from typing import Any
from unittest.mock import patch

from pyrainbird import encryption
import pytest

from homeassistant.components.rainbird import DOMAIN
from homeassistant.components.rainbird.const import (
    ATTR_DURATION,
    DEFAULT_TRIGGER_TIME_MINUTES,
)
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant

from tests.common import MockConfigEntry
from tests.test_util.aiohttp import AiohttpClientMocker, AiohttpClientMockResponse

HOST = "example.com"
URL = "http://example.com/stick"
PASSWORD = "password"
SERIAL_NUMBER = 0x12635436566
MAC_ADDRESS = "4C:A1:61:00:11:22"
MAC_ADDRESS_UNIQUE_ID = "4c:a1:61:00:11:22"

#
# Response payloads below come from pyrainbird test cases.
#

# Get serial number Command 0x85. Serial is 0x12635436566
SERIAL_RESPONSE = "850000012635436566"
ZERO_SERIAL_RESPONSE = "850000000000000000"
# Model and version command 0x82
MODEL_AND_VERSION_RESPONSE = "820005090C"  # ESP-TM2
# Get available stations command 0x83
AVAILABLE_STATIONS_RESPONSE = "83017F000000"  # Mask for 7 zones
EMPTY_STATIONS_RESPONSE = "830000000000"
# Get zone state command 0xBF.
ZONE_3_ON_RESPONSE = "BF0004000000"  # Zone 3 is on
ZONE_5_ON_RESPONSE = "BF0010000000"  # Zone 5 is on
ZONE_OFF_RESPONSE = "BF0000000000"  # All zones off
ZONE_STATE_OFF_RESPONSE = "BF0000000000"
# Get rain sensor state command 0XBE
RAIN_SENSOR_OFF = "BE00"
RAIN_SENSOR_ON = "BE01"
# Get rain delay command 0xB6
RAIN_DELAY = "B60010"  # 0x10 is 16
RAIN_DELAY_OFF = "B60000"
# ACK command 0x10, Echo 0x06
ACK_ECHO = "0106"
WIFI_PARAMS_RESPONSE = {
    "macAddress": MAC_ADDRESS,
    "localIpAddress": "1.1.1.38",
    "localNetmask": "255.255.255.0",
    "localGateway": "1.1.1.1",
    "rssi": -61,
    "wifiSsid": "wifi-ssid-name",
    "wifiPassword": "wifi-password-name",
    "wifiSecurity": "wpa2-aes",
    "apTimeoutNoLan": 20,
    "apTimeoutIdle": 20,
    "apSecurity": "unknown",
    "stickVersion": "Rain Bird Stick Rev C/1.63",
}


CONFIG = {
    DOMAIN: {
        "host": HOST,
        "password": PASSWORD,
        "trigger_time": {
            "minutes": 6,
        },
    }
}

CONFIG_ENTRY_DATA_OLD_FORMAT = {
    "host": HOST,
    "password": PASSWORD,
    "serial_number": SERIAL_NUMBER,
}
CONFIG_ENTRY_DATA = {
    "host": HOST,
    "password": PASSWORD,
    "serial_number": SERIAL_NUMBER,
    "mac": MAC_ADDRESS,
}


@pytest.fixture
def platforms() -> list[Platform]:
    """Fixture to specify platforms to test."""
    return []


@pytest.fixture
async def config_entry_unique_id() -> str:
    """Fixture for config entry unique id."""
    return MAC_ADDRESS_UNIQUE_ID


@pytest.fixture
async def serial_number() -> int:
    """Fixture for serial number used in the config entry data."""
    return SERIAL_NUMBER


@pytest.fixture
async def config_entry_data(serial_number: int) -> dict[str, Any]:
    """Fixture for MockConfigEntry data."""
    return {
        **CONFIG_ENTRY_DATA,
        "serial_number": serial_number,
    }


@pytest.fixture
async def config_entry(
    config_entry_data: dict[str, Any] | None,
    config_entry_unique_id: str | None,
) -> MockConfigEntry | None:
    """Fixture for MockConfigEntry."""
    if config_entry_data is None:
        return None
    return MockConfigEntry(
        unique_id=config_entry_unique_id,
        domain=DOMAIN,
        data=config_entry_data,
        options={ATTR_DURATION: DEFAULT_TRIGGER_TIME_MINUTES},
    )


@pytest.fixture(autouse=True)
async def add_config_entry(
    hass: HomeAssistant, config_entry: MockConfigEntry | None
) -> None:
    """Fixture to add the config entry."""
    if config_entry:
        config_entry.add_to_hass(hass)


@pytest.fixture(autouse=True)
def setup_platforms(
    hass: HomeAssistant,
    platforms: list[str],
) -> None:
    """Fixture for setting up the default platforms."""

    with patch(f"homeassistant.components.{DOMAIN}.PLATFORMS", platforms):
        yield


def rainbird_json_response(result: dict[str, str]) -> bytes:
    """Create a fake API response."""
    return encryption.encrypt(
        '{"jsonrpc": "2.0", "result": %s, "id": 1} ' % json.dumps(result),
        PASSWORD,
    )


def mock_json_response(result: dict[str, str]) -> AiohttpClientMockResponse:
    """Create a fake AiohttpClientMockResponse."""
    return AiohttpClientMockResponse(
        "POST", URL, response=rainbird_json_response(result)
    )


def mock_response(data: str) -> AiohttpClientMockResponse:
    """Create a fake AiohttpClientMockResponse."""
    return mock_json_response({"data": data})


def mock_response_error(
    status: HTTPStatus = HTTPStatus.SERVICE_UNAVAILABLE,
) -> AiohttpClientMockResponse:
    """Create a fake AiohttpClientMockResponse."""
    return AiohttpClientMockResponse("POST", URL, status=status)


@pytest.fixture(name="stations_response")
def mock_station_response() -> str:
    """Mock response to return available stations."""
    return AVAILABLE_STATIONS_RESPONSE


@pytest.fixture(name="zone_state_response")
def mock_zone_state_response() -> str:
    """Mock response to return zone states."""
    return ZONE_STATE_OFF_RESPONSE


@pytest.fixture(name="rain_response")
def mock_rain_response() -> str:
    """Mock response to return rain sensor state."""
    return RAIN_SENSOR_OFF


@pytest.fixture(name="rain_delay_response")
def mock_rain_delay_response() -> str:
    """Mock response to return rain delay state."""
    return RAIN_DELAY_OFF


@pytest.fixture(name="model_and_version_response")
def mock_model_and_version_response() -> str:
    """Mock response to return rain delay state."""
    return MODEL_AND_VERSION_RESPONSE


@pytest.fixture(name="api_responses")
def mock_api_responses(
    model_and_version_response: str,
    stations_response: str,
    zone_state_response: str,
    rain_response: str,
    rain_delay_response: str,
) -> list[str]:
    """Fixture to set up a list of fake API responsees for tests to extend.

    These are returned in the order they are requested by the update coordinator.
    """
    return [
        model_and_version_response,
        stations_response,
        zone_state_response,
        rain_response,
        rain_delay_response,
    ]


@pytest.fixture(name="responses")
def mock_responses(api_responses: list[str]) -> list[AiohttpClientMockResponse]:
    """Fixture to set up a list of fake API responsees for tests to extend."""
    return [mock_response(api_response) for api_response in api_responses]


@pytest.fixture(autouse=True)
def handle_responses(
    aioclient_mock: AiohttpClientMocker,
    responses: list[AiohttpClientMockResponse],
) -> None:
    """Fixture for command mocking for fake responses to the API url."""

    async def handle(method, url, data) -> AiohttpClientMockResponse:
        return responses.pop(0)

    aioclient_mock.post(URL, side_effect=handle)