using System.Text;
using Android.Bluetooth;

namespace InABox.Avalonia.Platform.Android;

public class Android_ConnectedBluetoothDevice : BluetoothGattCallback,IConnectedBluetoothDevice
{

    private BluetoothDevice? _device;
    public string ID { get; }
    public string Name { get; }
    public DateTime LastSeen { get; set; } = DateTime.Now;
    public byte[]? ManufacturerData { get; set; }

    private Guid[] _availableServices = [];
    public Guid[] AvailableServices => _availableServices;
        
    private BluetoothGatt? _bluetoothGatt;
    
    private TaskCompletionSource<bool>? _connectionTaskCompletionSource;
    private TaskCompletionSource<bool>? _serviceDiscoveryTaskCompletionSource;
    private TaskCompletionSource<byte[]>? _readTaskCompletionSource;
    private TaskCompletionSource<bool>? _writeTaskCompletionSource;

    public Android_ConnectedBluetoothDevice(BluetoothDevice? device)
    {
        _device = device;
        ID = device?.Address ?? string.Empty;
        Name = device?.Name ?? "Unknown Device";
    }

    public async Task<bool> ConnectAsync()
    {
        _connectionTaskCompletionSource = new TaskCompletionSource<bool>();
        _bluetoothGatt = _device?.ConnectGatt(Application.Context, false, this);
        return await _connectionTaskCompletionSource.Task;
    }

    public override void OnConnectionStateChange(BluetoothGatt? gatt, GattStatus status, ProfileState newState)
    {
        base.OnConnectionStateChange(gatt, status, newState);

        if (newState == ProfileState.Connected && status == GattStatus.Success)
        {
            Console.WriteLine("Connected to GATT server.");
            _connectionTaskCompletionSource?.TrySetResult(true);
        }
        else
        {
            Console.WriteLine("Failed to connect or disconnected.");
            _connectionTaskCompletionSource?.TrySetResult(false);
            Dispose();
        }
    }

    public async Task<bool> DiscoverServicesAsync()
    {
        _serviceDiscoveryTaskCompletionSource = new TaskCompletionSource<bool>();
        _bluetoothGatt?.DiscoverServices();
        _availableServices = _bluetoothGatt?.Services?.Select(x => Guid.Parse(x.Uuid?.ToString() ?? Guid.Empty.ToString())).ToArray() ?? [];
        return await _serviceDiscoveryTaskCompletionSource.Task;
    }

    public override void OnServicesDiscovered(BluetoothGatt? gatt, GattStatus status)
    {
        base.OnServicesDiscovered(gatt, status);

        if (status == GattStatus.Success)
        {
            Console.WriteLine("Services discovered.");
            _serviceDiscoveryTaskCompletionSource?.TrySetResult(true);
        }
        else
        {
            Console.WriteLine("Service discovery failed.");
            _serviceDiscoveryTaskCompletionSource?.TrySetResult(false);
        }
    }
    
    public async Task<byte[]?> ReadBytesAsync(Guid serviceid, Guid characteristicid)
    {
        byte[]? result = null;
        if (_bluetoothGatt != null)
        {
            var service = _bluetoothGatt.GetService(Java.Util.UUID.FromString(serviceid.ToString()));
            if (service != null)
            {
                var characteristic = service.GetCharacteristic(Java.Util.UUID.FromString(characteristicid.ToString()));
                if (characteristic != null)
                    result = await ReadCharacteristicAsync(characteristic);
            }
        }
        return result;
    }

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

    private async Task<byte[]?> ReadCharacteristicAsync(BluetoothGattCharacteristic characteristic)
    {
        if (_bluetoothGatt != null)
        {
            _readTaskCompletionSource = new TaskCompletionSource<byte[]>();

            if (!_bluetoothGatt.ReadCharacteristic(characteristic))
            {
                _readTaskCompletionSource.TrySetException(
                    new InvalidOperationException("Failed to initiate characteristic read."));
            }

            return await _readTaskCompletionSource.Task;
        }
        return null;
    }

    public override void OnCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] data, GattStatus status)
    {
        base.OnCharacteristicRead(gatt, characteristic, data, status);

        if (status == GattStatus.Success)
        {
            Console.WriteLine("Characteristic read successfully.");
            _readTaskCompletionSource?.TrySetResult(data);
        }
        else
        {
            Console.WriteLine("Failed to read characteristic.");
            _readTaskCompletionSource?.TrySetException(new InvalidOperationException("Characteristic read failed."));
        }
    }
    
    public async Task<bool> WriteBytesAsync(Guid serviceid, Guid characteristicid, byte[] data)
    {
        var result = false;
        if (_bluetoothGatt != null)
        {
            var service = _bluetoothGatt.GetService(Java.Util.UUID.FromString(serviceid.ToString()));
            if (service != null)
            {
                var characteristic = service.GetCharacteristic(Java.Util.UUID.FromString(characteristicid.ToString()));
                if (characteristic != null)
                    result = await WriteCharacteristicAsync(characteristic, data);
            }
        }

        return result;
    }
    
    public async Task<bool> WriteStringAsync(Guid serviceid, Guid characteristicid, string data)
    {
        var encoded = Encoding.UTF8.GetBytes(data);
        return await WriteBytesAsync(serviceid, characteristicid, encoded);
    }
    
    private async Task<bool> WriteCharacteristicAsync(BluetoothGattCharacteristic characteristic, byte[] data)
    {
        bool result = false;
        if (_bluetoothGatt != null)
        { 
            _writeTaskCompletionSource = new TaskCompletionSource<bool>();
            _bluetoothGatt.WriteCharacteristic(characteristic, data, 2); 
            result = await _writeTaskCompletionSource.Task;
        }
        return result;
    }

    public override void OnCharacteristicWrite(BluetoothGatt? gatt, BluetoothGattCharacteristic? characteristic, GattStatus status)
    {
        base.OnCharacteristicWrite(gatt, characteristic, status);
        if (status == GattStatus.Success)
        {
            Console.WriteLine("Characteristic written successfully.");
            _writeTaskCompletionSource?.TrySetResult(true);
        }
        else
        {
            Console.WriteLine("Failed to write characteristic.");
            _writeTaskCompletionSource?.TrySetException(new InvalidOperationException("Characteristic write failed."));
        }
    }
    
    public new void Dispose()
    {
        base.Dispose();
        try
        {
            _bluetoothGatt?.Disconnect();
            _bluetoothGatt?.Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error during disposal: {ex.Message}");
        }
        
        _bluetoothGatt?.Dispose();
        _bluetoothGatt = null;
        
        _device?.Dispose();
        _device = null;

        Console.WriteLine("Resources released.");
        
    }
    
}