using System;
using System.Text;
using System.Threading.Tasks;
using Plugin.BLE.Abstractions;

namespace InABox.Avalonia.Platform.iOS;

public class iOS_ConnectedBluetoothDevice(IBluetoothDevice device)
    : IConnectedBluetoothDevice
{
    public void Dispose()
    {
        Device.Dispose();
    }

    public iOS_BluetoothDevice Device { get; } = (iOS_BluetoothDevice)device;

    public string ID => Device.ID;
    public string Name => Device.Name;
    public Guid[] AvailableServices => Device.AvailableServices;

    public DateTime LastSeen
    {
        get => Device.LastSeen; 
        set => device.LastSeen = value;
    }

    public byte[]? ManufacturerData
    {
        get => Device.ManufacturerData; 
        set => Device.ManufacturerData = value;
    }
    
    public async Task<bool> WriteBytesAsync(Guid serviceid, Guid characteristicid, byte[] data)
    {
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} WriteBytesAsync::Getting Service {serviceid}...");
        using (var service = await Device.Native.GetServiceAsync(serviceid))
        {
            if (service == null)
            {
                System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} WriteBytesAsync::Failed to find service");
                return false;
            }

            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} WriteBytesAsync::Getting Characteritsic {characteristicid}...");
            var characteristic = await service.GetCharacteristicAsync(characteristicid);
            
            if (characteristic == null)
            {
                System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} WriteBytesAsync::Failed to find characteristic");
                return false;
            }

            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} WriteBytesAsync::Writing [{string.Join(" ",data.Select(x=>$"{x:X2}"))}]...");
            
            // for some reason the async call fails randomly
            //var result = await characteristic.WriteAsync(data);
            
            // So lets hack in a one-second timeout...
            var writeTask = characteristic.WriteAsync(data);
            var waitTask = Task.Delay(1000);
            await Task.WhenAny(writeTask, waitTask);
            if (writeTask.Status == TaskStatus.RanToCompletion)
            {
                System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} WriteBytesAsync::Wrote {writeTask.Result} bytes.");
                return writeTask.Result == data.Length;
            }
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} WriteBytesAsync::Timeout while writing!");
            return false;
        }
    }

    public async Task<bool> WriteStringAsync(Guid serviceid, Guid characteristicid, string data)
    {
        var encoded = Encoding.UTF8.GetBytes(data);
        return await WriteBytesAsync(serviceid, characteristicid, encoded);
    }

    public async Task<byte[]?> ReadBytesAsync(Guid serviceid, Guid characteristicid)
    {
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} ReadBytesAsync::Getting Service {serviceid}...");
        using (var service = await Device.Native.GetServiceAsync(serviceid))
        {
            if (service == null)
            {
                System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} ReadBytesAsync::Failed to find service");
                return [];
            }

            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} ReadBytesAsync::Getting Characteritsic {characteristicid}...");
            var characteristic = await service.GetCharacteristicAsync(characteristicid);
            
            if (characteristic == null)
            {
                System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} ReadBytesAsync::Failed to find characteristic");
                return [];
            }

            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} ReadBytesAsync::Reading data...");
            var result = await characteristic.ReadAsync();
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} ReadBytesAsync:: [{result.resultCode}] => [{string.Join(" ",result.data.Select(x=>$"{x:X2}"))}].");
            
            return result.data;
            
        }
        
    }

    public async Task<string?> ReadStringAsync(Guid serviceid, Guid characteristicid)
    {
        var data = await ReadBytesAsync(serviceid,characteristicid);
        return data != null ? Encoding.UTF8.GetString(data) : null;
    }
}