"""Tests for the Snooz component."""
from __future__ import annotations

from dataclasses import dataclass
from unittest.mock import patch

from pysnooz.commands import SnoozCommandData
from pysnooz.device import DisconnectionReason
from pysnooz.testing import MockSnoozDevice as ParentMockSnoozDevice

from homeassistant.components.snooz.const import DOMAIN
from homeassistant.const import CONF_ADDRESS, CONF_TOKEN
from homeassistant.core import HomeAssistant
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo

from tests.common import MockConfigEntry
from tests.components.bluetooth import generate_ble_device

TEST_ADDRESS = "00:00:00:00:AB:CD"
TEST_SNOOZ_LOCAL_NAME = "Snooz-ABCD"
TEST_SNOOZ_DISPLAY_NAME = "Snooz ABCD"
TEST_PAIRING_TOKEN = "deadbeef"

NOT_SNOOZ_SERVICE_INFO = BluetoothServiceInfo(
    name="Definitely not snooz",
    address=TEST_ADDRESS,
    rssi=-63,
    manufacturer_data={3234: b"\x00\x01"},
    service_data={},
    service_uuids=[],
    source="local",
)

SNOOZ_SERVICE_INFO_PAIRING = BluetoothServiceInfo(
    name=TEST_SNOOZ_LOCAL_NAME,
    address=TEST_ADDRESS,
    rssi=-63,
    manufacturer_data={65552: bytes([4]) + bytes.fromhex(TEST_PAIRING_TOKEN)},
    service_uuids=[
        "80c37f00-cc16-11e4-8830-0800200c9a66",
        "90759319-1668-44da-9ef3-492d593bd1e5",
    ],
    service_data={},
    source="local",
)

SNOOZ_SERVICE_INFO_NOT_PAIRING = BluetoothServiceInfo(
    name=TEST_SNOOZ_LOCAL_NAME,
    address=TEST_ADDRESS,
    rssi=-63,
    manufacturer_data={65552: bytes([4]) + bytes([0] * 8)},
    service_uuids=[
        "80c37f00-cc16-11e4-8830-0800200c9a66",
        "90759319-1668-44da-9ef3-492d593bd1e5",
    ],
    service_data={},
    source="local",
)


@dataclass
class SnoozFixture:
    """Snooz test fixture."""

    entry: MockConfigEntry
    device: MockSnoozDevice


class MockSnoozDevice(ParentMockSnoozDevice):
    """Used for testing integration with Bleak.

    Adjusted for https://github.com/AustinBrunkhorst/pysnooz/issues/6
    """

    def _on_device_disconnected(self, e) -> None:
        if self._is_manually_disconnecting:
            e.kwargs.set("reason", DisconnectionReason.USER)
        return super()._on_device_disconnected(e)


async def create_mock_snooz(
    connected: bool = True,
    initial_state: SnoozCommandData = SnoozCommandData(on=False, volume=0),
) -> MockSnoozDevice:
    """Create a mock device."""

    ble_device = SNOOZ_SERVICE_INFO_NOT_PAIRING
    device = MockSnoozDevice(ble_device, initial_state=initial_state)

    # execute a command to initiate the connection
    if connected is True:
        await device.async_execute_command(initial_state)

    return device


async def create_mock_snooz_config_entry(
    hass: HomeAssistant, device: MockSnoozDevice
) -> MockConfigEntry:
    """Create a mock config entry."""

    with patch(
        "homeassistant.components.snooz.SnoozDevice", return_value=device
    ), patch(
        "homeassistant.components.snooz.async_ble_device_from_address",
        return_value=generate_ble_device(device.address, device.name),
    ):
        entry = MockConfigEntry(
            domain=DOMAIN,
            unique_id=TEST_ADDRESS,
            data={CONF_ADDRESS: TEST_ADDRESS, CONF_TOKEN: TEST_PAIRING_TOKEN},
        )
        entry.add_to_hass(hass)

        assert await hass.config_entries.async_setup(entry.entry_id)
        await hass.async_block_till_done()

        return entry
