using Android.OS;
using Com.Honeywell.Aidc;
using SteUp.Shared.Core.BarcodeReader.Dto;
using SteUp.Shared.Core.BarcodeReader.Enum;

namespace SteUp.Maui.Core.System;

public partial class HoneywellScannerService
{
    private AidcManager? _aidcManager;
    private BarcodeReader? _barcodeReader;

    private static readonly List<string> CompatibleModels = ["EDA50", "EDA51", "EDA52", "CT60", "CT40", "CT40XP", "CT70-X1"];

    public partial bool IsRightAdapter()
    {
        return CompatibleModels.Contains(Build.Model ?? string.Empty);
    }

    public partial string GetAdapterName() => "Honeywell";

    public partial void Init(Action onDeviceReady)
    {
        if (!IsRightAdapter())
            throw new Exception($"Adapter non trovato per il modello: {Build.Model}");

        AidcManager.Create(
            Android.App.Application.Context,
            new AidcCreatedCallback(this, onDeviceReady)
        );
    }

    public partial void Deinit()
    {
        _barcodeReader?.Close();
        _barcodeReader = null;

        _aidcManager?.Close();
        _aidcManager = null;
    }

    public partial void OnKeyEvent(object keyEvent)
    {
        // Honeywell gestisce il trigger hardware internamente, non serve implementazione
    }

    public partial void ChangeSettings(List<(string Key, object Value)> settings)
    {
        if (_barcodeReader == null) return;

        var properties = GetDefaultProperties();

        foreach (var (key, value) in settings)
        {
            switch (key)
            {
                case "TRIGGER_SCAN_MODE":
                    var scanMode = (string)value switch
                    {
                        "ONE_SHOT"                     => BarcodeReader.TriggerScanModeOneshot,
                        "CONTINUOUS"                   => BarcodeReader.TriggerScanModeContinuous,
                        "READ_ON_SECOND_TRIGGER_PRESS" => BarcodeReader.TriggerScanModeReadOnSecondTriggerPress,
                        "READ_ON_RELEASE"              => BarcodeReader.TriggerScanModeReadOnRelease,
                        _                              => BarcodeReader.TriggerScanModeOneshot
                    };
                    properties[BarcodeReader.PropertyTriggerScanMode] = scanMode!;
                    break;

                case "TRIGGER_SCAN_DELAY":
                    properties[BarcodeReader.PropertyTriggerScanDelay] = int.Parse((string)value);
                    break;
            }
        }

        _barcodeReader.SetProperties(properties);

        try { _barcodeReader.Claim(); }
        catch (ScannerUnavailableException ex) { OnScanFailed?.Invoke(ex); }
    }

    private void RegisterListenersInternal()
    {
        _barcodeReader?.AddBarcodeListener(new BarcodeEventListener(this));
    }

    private void DispatchEvent(BarcodeReadEvent e)
    {
        var dto = new BarcodeScanDto
        {
            StringValue = e.BarcodeData,
            Type        = MapCodeId(e.CodeId),
            Name        = e.CodeId
        };

        OnScanSuccessful?.Invoke(dto);
    }

    // Honeywell restituisce il simbologia tramite un Code ID di un solo carattere (getCodeId),
    // non un enum. Lo mappiamo su BarcodeType. Ai fini dell'app conta distinguere
    // EAN13 / CODE128 / EAN128 (vedi BarcodeManager.IsEanPeso); il resto è best-effort.
    private static BarcodeType MapCodeId(string? codeId) => codeId switch
    {
        "a" => BarcodeType.CODABAR,
        "b" => BarcodeType.CODE39,
        "c" => BarcodeType.UPCA,
        "d" => BarcodeType.EAN13,
        "D" => BarcodeType.EAN8,
        "e" => BarcodeType.INTERLEAVED_2OF5,
        "E" => BarcodeType.UPCE,
        "g" => BarcodeType.MSI,
        "h" => BarcodeType.CODE11,
        "i" => BarcodeType.CODE93,
        "j" => BarcodeType.CODE128,   // Code 128 e GS1-128 condividono il Code ID 'j'
        "I" => BarcodeType.EAN128,    // alcune generazioni riportano GS1-128 come 'I'
        "r" => BarcodeType.PDF417,
        "R" => BarcodeType.MICRO_PDF417,
        "s" => BarcodeType.QR,
        "w" => BarcodeType.DATAMATRIX,
        "x" => BarcodeType.MAXICODE,
        "y" => BarcodeType.GS1_DATABAR_14,
        "z" => BarcodeType.AZTEC,
        _   => BarcodeType.NIL
    };

    private static Dictionary<string, Java.Lang.Object> GetDefaultProperties() => new()
    {
        { BarcodeReader.PropertyEan8Enabled,                    true },
        { BarcodeReader.PropertyEan13Enabled,                   true },
        { BarcodeReader.PropertyCode39Enabled,                  true },
        { BarcodeReader.PropertyCode128Enabled,                 true },
        { BarcodeReader.PropertyGs1128Enabled,                  true },
        { BarcodeReader.PropertyUpcAEnable,                     true },
        { BarcodeReader.PropertyEan8CheckDigitTransmitEnabled,  true },
        { BarcodeReader.PropertyEan13CheckDigitTransmitEnabled, true },
        { BarcodeReader.PropertyUpcACheckDigitTransmitEnabled,  true }
    };

    private class AidcCreatedCallback(HoneywellScannerService service, Action onDeviceReady)
        : Java.Lang.Object, AidcManager.ICreatedCallback
    {
        public void OnCreated(AidcManager? manager)
        {
            service._aidcManager = manager;

            try
            {
                if (manager == null) throw new Exception("AidcManager null");
                
                service._barcodeReader = manager.CreateBarcodeReader();
                service.RegisterListenersInternal();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                
                service.OnScanFailed?.Invoke(ex);
            }

            onDeviceReady.Invoke();
        }
    }

    private class BarcodeEventListener(HoneywellScannerService service)
        : Java.Lang.Object, BarcodeReader.IBarcodeListener
    {
        public void OnBarcodeEvent(BarcodeReadEvent? e)   => service.DispatchEvent(e!);
        public void OnFailureEvent(BarcodeFailureEvent? e) =>
            service.OnScanFailed?.Invoke(new Exception($"Scan failure: {e}"));
    }
}