using Android.Bluetooth;
using Android.Bluetooth.LE;
using Android.Content;
using InABox.Core;
using Microsoft.Maui.ApplicationModel;

namespace InABox.Avalonia.Platform.Android;

public class Android_Bluetooth : IBluetooth
{
    public Logger? Logger { get; set; }

    public CoreObservableCollection<IBluetoothDevice> Devices { get; private set; } = new CoreObservableCollection<IBluetoothDevice>();
    
    private readonly BluetoothLeScanner? _scanner;

    public event EventHandler? Changed;

    public Android_Bluetooth()
    {
        var _manager = Application.Context.GetSystemService(Context.BluetoothService) as BluetoothManager;
        var _adapter = _manager?.Adapter;
        _scanner = _adapter?.BluetoothLeScanner;
        
        Task.Run(() =>
        {
            while (true)
            {
                var stale = Devices.ToArray().Where(x => (x == null) || (x.LastSeen < DateTime.Now.Subtract(new TimeSpan(0, 0, 5))))
                    .ToArray();
                if (stale.Any())
                {
                    Devices.RemoveRange(stale);
                    Changed?.Invoke(this, EventArgs.Empty);
                }
                    
                Task.Delay(500);
            }
        });
    }
    
    public static async Task<bool> IsPermitted<TPermission>() where TPermission : Permissions.BasePermission, new()
    {
        try
        {
            PermissionStatus status = await Permissions.CheckStatusAsync<TPermission>();
            if (status == PermissionStatus.Granted)
                return true;
            var request = await Permissions.RequestAsync<TPermission>();
            return request == PermissionStatus.Granted;

        }
        catch (TaskCanceledException ex)
        {
            return false;
        }
    }

    public async Task<bool> IsAvailable()
    {
        if (await IsPermitted<Permissions.Bluetooth>()) 
            return _scanner != null;
        return false;
    }
    BluetoothScanManager? _callback;
    
    public async Task<bool> StartScanningAsync(Guid configServiceId)
    {
        if (await IsAvailable())
        {
            _callback = new BluetoothScanManager((d) => DoDeviceFound(d, configServiceId), ScanStopped);
            _scanner!.StartScan(_callback);
            return true;
        }
        return false;
    }

    public async Task<bool> StopScanningAsync()
    {
        if (await IsAvailable())
        {
            if (_callback != null)
            {
                _scanner!.StopScan(_callback);
                return true;
            }
        }
        return false;
    }
    

    private void DoDeviceFound(ScanResult device, Guid configServiceId)
    {

        var isTarget = device.ScanRecord?.ServiceUuids?
            .Any(x=>string.Equals(x.ToString(),configServiceId.ToString(), StringComparison.OrdinalIgnoreCase)) 
                       ?? false;
        if (isTarget)
        {
            var abd = Devices.FirstOrDefault(x => x.ID == device.Device?.Address);
            if (abd == null)
            {

                if (isTarget)
                {
                    var services = device.ScanRecord?.ServiceUuids?
                        .Select(x => Guid.Parse(x.ToString()))
                        .Where(x => !x.ToString().ToUpper().EndsWith("-0000-1000-8000-00805F9B34FB") &&
                                    configServiceId != x)
                        .ToArray() ?? [];

                    if (services.Any())
                    {
                        abd = new Android_BluetoothDevice(device, services, DateTime.Now);
                        Devices.Add(abd);
                        Changed?.Invoke(this, EventArgs.Empty);
                    }
                }
            }
            else
                abd.LastSeen = DateTime.Now;
        }

    }

    private void ScanStopped()
    {
        _callback = null;
    }

    public async Task<IConnectedBluetoothDevice?> Connect(IBluetoothDevice device)
    {
        if (device is Android_BluetoothDevice d && d.Scan.Device is BluetoothDevice bd)
        {
            var result = new Android_ConnectedBluetoothDevice(bd);
            if (await result.ConnectAsync())
            {
                await result.DiscoverServicesAsync();
                return result;
            }
        }
        return null;
    }
    
        
    public async Task<bool> Disconnect(IConnectedBluetoothDevice device)
    {
        if (device is Android_ConnectedBluetoothDevice d)
            d.Dispose();
        
        return await Task.FromResult(true);
    }
}