# Copyright 2021-2025 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.

# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
from __future__ import annotations

import pytest

from bumble import device, gatt
from bumble.profiles import gatt_service

from . import test_utils


# -----------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_database_hash():
    devices = await test_utils.TwoDevices.create_with_connection()
    devices[0].gatt_server.services.clear()
    devices[0].gatt_server.attributes.clear()
    devices[0].gatt_server.attributes_by_handle.clear()
    devices[0].add_service(
        gatt.Service(
            gatt.GATT_GENERIC_ACCESS_SERVICE,
            characteristics=[
                gatt.Characteristic(
                    gatt.GATT_DEVICE_NAME_CHARACTERISTIC,
                    (
                        gatt.Characteristic.Properties.READ
                        | gatt.Characteristic.Properties.WRITE
                    ),
                    gatt.Characteristic.Permissions.READ_REQUIRES_AUTHENTICATION,
                ),
                gatt.Characteristic(
                    gatt.GATT_APPEARANCE_CHARACTERISTIC,
                    gatt.Characteristic.Properties.READ,
                    gatt.Characteristic.Permissions.READ_REQUIRES_AUTHENTICATION,
                ),
            ],
        )
    )
    devices[0].add_service(
        gatt_service.GenericAttributeProfileService(
            server_supported_features=None,
            database_hash_enabled=True,
            service_change_enabled=True,
        )
    )
    devices[0].gatt_server.add_attribute(
        gatt.Service(gatt.GATT_GLUCOSE_SERVICE, characteristics=[])
    )
    # There is a special attribute order in the spec, so we need to add attribute one by
    # one here.
    battery_service = gatt.Service(
        gatt.GATT_BATTERY_SERVICE,
        characteristics=[
            gatt.Characteristic(
                gatt.GATT_BATTERY_LEVEL_CHARACTERISTIC,
                properties=gatt.Characteristic.Properties.READ,
                permissions=gatt.Characteristic.Permissions.READ_REQUIRES_AUTHENTICATION,
            )
        ],
        primary=False,
    )
    battery_service.handle = 0x0014
    battery_service.end_group_handle = 0x0016
    devices[0].gatt_server.add_attribute(
        gatt.IncludedServiceDeclaration(battery_service)
    )
    c = gatt.Characteristic(
        '2A18',
        properties=(
            gatt.Characteristic.Properties.READ
            | gatt.Characteristic.Properties.INDICATE
            | gatt.Characteristic.Properties.EXTENDED_PROPERTIES
        ),
        permissions=gatt.Characteristic.Permissions.READ_REQUIRES_AUTHENTICATION,
    )
    devices[0].gatt_server.add_attribute(
        gatt.CharacteristicDeclaration(c, devices[0].gatt_server.next_handle() + 1)
    )
    devices[0].gatt_server.add_attribute(c)
    devices[0].gatt_server.add_attribute(
        gatt.Descriptor(
            gatt.GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR,
            gatt.Descriptor.Permissions.READ_REQUIRES_AUTHENTICATION,
            b'\x02\x00',
        ),
    )
    devices[0].gatt_server.add_attribute(
        gatt.Descriptor(
            gatt.GATT_CHARACTERISTIC_EXTENDED_PROPERTIES_DESCRIPTOR,
            gatt.Descriptor.Permissions.READ_REQUIRES_AUTHENTICATION,
            b'\x00\x00',
        ),
    )
    devices[0].add_service(battery_service)

    peer = device.Peer(devices.connections[1])
    client = await peer.discover_service_and_create_proxy(
        gatt_service.GenericAttributeProfileServiceProxy
    )
    assert client.database_hash_characteristic
    assert await client.database_hash_characteristic.read_value() == bytes.fromhex(
        'F1CA2D48ECF58BAC8A8830BBB9FBA990'
    )


# -----------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_service_changed():
    devices = await test_utils.TwoDevices.create_with_connection()
    assert (service := devices[0].gatt_service)

    peer = device.Peer(devices.connections[1])
    assert (
        client := await peer.discover_service_and_create_proxy(
            gatt_service.GenericAttributeProfileServiceProxy
        )
    )
    assert client.service_changed_characteristic
    indications = []
    await client.service_changed_characteristic.subscribe(
        indications.append, prefer_notify=False
    )
    await devices[0].indicate_subscribers(
        service.service_changed_characteristic, b'1234'
    )
    await test_utils.async_barrier()
    assert indications[0] == b'1234'
