using InABox.Core;
using Plugin.BLE;
using Plugin.BLE.Abstractions;
using Plugin.BLE.Abstractions.Contracts;
using Plugin.BLE.Abstractions.EventArgs;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace InABox.Avalonia.Platform.iOS;


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

    public event EventHandler? Changed;
    public CoreObservableCollection<IBluetoothDevice> Devices { get; } = new ();
    
    private IBluetoothLE _ble;
    private IAdapter _adapter;
    private ScanFilterOptions _scanoptions = new ScanFilterOptions();
    private StaleItemMonitor<IBluetoothDevice> _staleItemMonitor;
    public iOS_Bluetooth()
    {
        
        _staleItemMonitor = new StaleItemMonitor<IBluetoothDevice>(
            Devices,
            x=>x.LastSeen,
            x =>x.ID,
            TimeSpan.FromSeconds(10), 
            TimeSpan.FromMilliseconds(500)
        );
        
        _ble = CrossBluetoothLE.Current; 
        _adapter = CrossBluetoothLE.Current.Adapter;
        _adapter.ScanTimeout = 5000;
        _adapter.ScanTimeoutElapsed += async (sender, args) => { await _adapter.StartScanningForDevicesAsync(_scanoptions); };
        _adapter.DeviceDiscovered += AdapterOnDeviceDiscovered;
        //_adapter.ScanMatchMode = ScanMatchMode.AGRESSIVE;
        //_adapter.ScanMode = ScanMode.LowLatency;
        
        // Task.Run(() =>
        // {
        //     while (true)
        //     {
        //         var stale = Devices.ToArray().Where(x => (x == null) || (x.LastSeen < DateTime.Now.Subtract(new TimeSpan(0, 0, 10))))
        //             .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 _adapter != null;
    }

    public async Task<bool> StartScanningAsync(Guid configServiceId)
    {
        
        _scanoptions.ServiceUuids = [configServiceId];
        return await StartScanningAsync();
    }

    private async Task<bool> StartScanningAsync()
    {
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Starting Bluetooth Scanning...");
        await _staleItemMonitor.StartAsync();
        await _adapter.StartScanningForDevicesAsync(_scanoptions);
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Bluetooth Scanning Started.");
        return true;
    }

    private void AdapterOnDeviceDiscovered(object? sender, DeviceEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Found device {e.Device.Name}...");
        var device = Devices.FirstOrDefault(x => string.Equals(x.ID, e.Device.Id.ToString()));
        if (device == null)
        {
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Adding device {e.Device.Id}...");
            device = new iOS_BluetoothDevice(e.Device);
            Devices.Add(device);
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Added device {device.ID}.");
        }

        device.LastSeen = DateTime.Now;
        device.ManufacturerData = e.Device.AdvertisementRecords
            .FirstOrDefault(x => x.Type == AdvertisementRecordType.ManufacturerSpecificData)?.Data ?? [];
        
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Notifying Changes...");
        Changed?.Invoke(this, EventArgs.Empty);
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Changes Notified.");
    }
    

    public async Task<bool> StopScanningAsync()
    {
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stopping Bluetooth Scanning...");
        await _staleItemMonitor.StopAsync();
        await _adapter.StopScanningForDevicesAsync();
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Bluetooth Scanning Stopped.");
        return true;
    }

    public async Task<IConnectedBluetoothDevice?> Connect(IBluetoothDevice device)
    {
        if (Guid.TryParse(device.ID, out var deviceid))
        {
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Connecting Bluetooth Device {device.ID}...");
            await StopScanningAsync();
            var connected = await _adapter.ConnectToKnownDeviceAsync(deviceid);
            if (connected != null)
            {
                var result = new iOS_ConnectedBluetoothDevice(device);
                System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Bluetooth Device {device.ID} connected.");
                return result;
            }
            await StartScanningAsync();
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Connection to {device.ID} failed.");
        }
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Device has invalid ID.");
        return null;
    }

    public async Task<bool> Disconnect(IConnectedBluetoothDevice device)
    {
        if (device is iOS_ConnectedBluetoothDevice connected)
        {
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Disconnecting Bluetooth Device {device.ID}...");
            await _adapter.DisconnectDeviceAsync(connected.Device.Native);
            await StartScanningAsync();
            System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Bluetooth Device {device.ID} disconnected.");
        }
        return true;
    }

}