# Copyright 2021-2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import asyncio
import struct
from unittest import mock

import pytest

from bumble import device as bumble_device
from bumble.profiles import asha

from .test_utils import TwoDevices

# -----------------------------------------------------------------------------
HI_SYNC_ID = b'\x00\x01\x02\x03\x04\x05\x06\x07'
TIMEOUT = 0.1


# -----------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_get_only_properties():
    devices = TwoDevices()
    await devices.setup_connection()

    asha_service = asha.AshaService(
        hisyncid=HI_SYNC_ID,
        device=devices[0],
        protocol_version=0x01,
        capability=0x02,
        feature_map=0x03,
        render_delay_milliseconds=0x04,
        supported_codecs=0x05,
    )
    devices[0].add_service(asha_service)

    async with bumble_device.Peer(devices.connections[1]) as peer:
        asha_client = peer.create_service_proxy(asha.AshaServiceProxy)
        assert asha_client

        read_only_properties = (
            await asha_client.read_only_properties_characteristic.read_value()
        )
        (
            protocol_version,
            capabilities,
            hi_sync_id,
            feature_map,
            render_delay_milliseconds,
            _,
            supported_codecs,
        ) = struct.unpack("<BB8sBHHH", read_only_properties)
        assert protocol_version == 0x01
        assert capabilities == 0x02
        assert hi_sync_id == HI_SYNC_ID
        assert feature_map == 0x03
        assert render_delay_milliseconds == 0x04
        assert supported_codecs == 0x05


# -----------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_get_psm():
    devices = TwoDevices()
    await devices.setup_connection()

    asha_service = asha.AshaService(
        hisyncid=HI_SYNC_ID,
        device=devices[0],
        capability=0,
    )
    devices[0].add_service(asha_service)

    async with bumble_device.Peer(devices.connections[1]) as peer:
        asha_client = peer.create_service_proxy(asha.AshaServiceProxy)
        assert asha_client

        psm = (await asha_client.psm_characteristic.read_value())[0]
        assert psm == asha_service.psm


# -----------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_write_audio_control_point_start():
    devices = TwoDevices()
    await devices.setup_connection()

    asha_service = asha.AshaService(
        hisyncid=HI_SYNC_ID,
        device=devices[0],
        capability=0,
    )
    devices[0].add_service(asha_service)

    async with bumble_device.Peer(devices.connections[1]) as peer:
        asha_client = peer.create_service_proxy(asha.AshaServiceProxy)
        assert asha_client
        status_notifications = asyncio.Queue()
        await asha_client.audio_status_point_characteristic.subscribe(
            status_notifications.put_nowait
        )

        start_cb = mock.MagicMock()
        asha_service.on('started', start_cb)
        await asha_client.audio_control_point_characteristic.write_value(
            bytes(
                [asha.OpCode.START, asha.Codec.G_722_16KHZ, asha.AudioType.MEDIA, 0, 1]
            )
        )
        status = (await asyncio.wait_for(status_notifications.get(), TIMEOUT))[0]
        assert status == asha.AudioStatus.OK

        start_cb.assert_called_once()
        assert asha_service.active_codec == asha.Codec.G_722_16KHZ
        assert asha_service.volume == 0
        assert asha_service.other_state == 1
        assert asha_service.audio_type == asha.AudioType.MEDIA


# -----------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_write_audio_control_point_stop():
    devices = TwoDevices()
    await devices.setup_connection()

    asha_service = asha.AshaService(
        hisyncid=HI_SYNC_ID,
        device=devices[0],
        capability=0,
    )
    devices[0].add_service(asha_service)

    async with bumble_device.Peer(devices.connections[1]) as peer:
        asha_client = peer.create_service_proxy(asha.AshaServiceProxy)
        assert asha_client
        status_notifications = asyncio.Queue()
        await asha_client.audio_status_point_characteristic.subscribe(
            status_notifications.put_nowait
        )

        stop_cb = mock.MagicMock()
        asha_service.on('stopped', stop_cb)
        await asha_client.audio_control_point_characteristic.write_value(
            bytes([asha.OpCode.STOP])
        )
        status = (await asyncio.wait_for(status_notifications.get(), TIMEOUT))[0]
        assert status == asha.AudioStatus.OK

        stop_cb.assert_called_once()
        assert asha_service.active_codec is None
        assert asha_service.volume is None
        assert asha_service.other_state is None
        assert asha_service.audio_type is None
