using System.Collections.Generic;
using System.Text;
using Aidlab;
using Aidlab.BLE;
using UnityEngine;

namespace Aidlab.Examples
{
    public sealed class Example : MonoBehaviour
    {
        [SerializeField] private bool autoInitSdk = true;
        [SerializeField] private string targetDeviceName = "Aidlab";
        [SerializeField] private Signal[] liveSignalsToCollect =
            { Signal.Ecg, Signal.Temperature, Signal.HeartRate, Signal.Rr };
        [SerializeField] private Signal[] syncSignalsToStore = new Signal[0];
        [SerializeField] private float refreshIntervalSeconds = 0.25f;
        [SerializeField] private bool showDiagnostics = false;

        private AidlabSDK sdk;
        private bool subscribed;
        private float lastRefresh;
        private string statusText = "";
        private string devicesText = "";
        private string signalsText = "";
        private GUIStyle panelStyle;
        private GUIStyle headerStyle;
        private GUIStyle bodyStyle;

        private void Start()
        {
            EnsureSdkInstance();
            SubscribeSignalsOnce();
            RefreshOverlay();
        }

        private void Update()
        {
            EnsureSdkInstance();
            SubscribeSignalsOnce();

            if (sdk != null && sdk.TargetDeviceName != targetDeviceName)
                sdk.SetTargetDeviceName(targetDeviceName);

            if (sdk != null &&
                (!SignalsEqual(sdk.LiveSignalsToCollect, liveSignalsToCollect) ||
                 !SignalsEqual(sdk.SyncSignalsToStore, syncSignalsToStore)))
            {
                sdk.SetCollectSignals(liveSignalsToCollect, syncSignalsToStore);
            }

            if (Time.unscaledTime - lastRefresh < refreshIntervalSeconds)
                return;

            lastRefresh = Time.unscaledTime;
            RefreshOverlay();
        }

        private void OnGUI()
        {
            EnsureStyles();
            GUILayout.BeginArea(new Rect(12f, 12f, 640f, 500f), panelStyle);
            GUILayout.Label("Aidlab Unity Example", headerStyle);
            GUILayout.Label(statusText, bodyStyle);
            GUILayout.Label(devicesText, bodyStyle);
            GUILayout.Label(signalsText, bodyStyle);
            GUILayout.EndArea();
        }

        private void OnDestroy()
        {
            if (!subscribed)
                return;

            AidlabSDK.aidlabDelegate.temperature.Unsubscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.wearState.Unsubscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.heartRate.Unsubscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.respiration.Unsubscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.signalQuality.Unsubscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.eda.Unsubscribe(RefreshOverlay);
            AidlabSDK.aidlabSyncDelegate.unsynchronizedSize.Unsubscribe(RefreshOverlay);
            AidlabSDK.aidlabSyncDelegate.syncState.Unsubscribe(RefreshOverlay);
            subscribed = false;
        }

        private void EnsureSdkInstance()
        {
            if (sdk != null)
                return;

            sdk = FindAnyObjectByType<AidlabSDK>();
            if (sdk != null || !autoInitSdk)
                return;

            AidlabSDK.init(targetDeviceName);
            sdk = FindAnyObjectByType<AidlabSDK>();
            if (sdk == null)
                autoInitSdk = false;
        }

        private void SubscribeSignalsOnce()
        {
            if (subscribed)
                return;

            AidlabSDK.aidlabDelegate.temperature.Subscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.wearState.Subscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.heartRate.Subscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.respiration.Subscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.signalQuality.Subscribe(RefreshOverlay);
            AidlabSDK.aidlabDelegate.eda.Subscribe(RefreshOverlay);
            AidlabSDK.aidlabSyncDelegate.unsynchronizedSize.Subscribe(RefreshOverlay);
            AidlabSDK.aidlabSyncDelegate.syncState.Subscribe(RefreshOverlay);
            subscribed = true;
        }

        private void RefreshOverlay()
        {
            var status = new StringBuilder();
            status.AppendLine($"Platform: {Application.platform}");
            status.AppendLine($"Target name: {(sdk != null ? sdk.TargetDeviceName : "n/a")}");
            status.AppendLine($"Live: {FormatSignals(liveSignalsToCollect)}");
            status.AppendLine($"Sync: {FormatSignals(syncSignalsToStore)}");
            status.AppendLine($"Transport: {(sdk != null ? sdk.TransportStatus : "n/a")}");

            if (sdk != null && !string.IsNullOrEmpty(sdk.TransportLastError))
                status.AppendLine($"BLE error: {sdk.TransportLastError}");

            status.AppendLine($"SDK initialized: {(sdk != null && sdk.IsSdkInitialized ? "yes" : "no")}");
            status.AppendLine($"Firmware: {(sdk != null ? sdk.firmwareRevisionStr : "")}");
            status.AppendLine($"Hardware: {(sdk != null ? sdk.hardwareRevisionStr : "")}");
            status.AppendLine(
                $"Last packet: {(sdk != null && sdk.SecondsSinceLastPacket >= 0 ? $"{sdk.SecondsSinceLastPacket:0.0}s ago" : "never")}"
            );

            if (sdk != null && showDiagnostics)
            {
                long lastRxAt = sdk.DebugLastRxAtMs;
                long ageMs = lastRxAt > 0 ? AidlabSDK.DebugNowMs() - lastRxAt : -1;
                status.AppendLine("Diagnostics:");
                status.AppendLine(
                    $"RX: {sdk.DebugRxPackets} packets, {sdk.DebugRxBytes} bytes; " +
                    $"signal callbacks: {sdk.DebugSignalUpdates}; lastRxAgeMs={ageMs}"
                );
            }

            statusText = status.ToString();
            devicesText = BuildDevicesText();
            signalsText = BuildSignalsText();
        }

        private string BuildDevicesText()
        {
            if (sdk == null)
                return "Devices: (SDK not created yet)";

            if (Application.platform == RuntimePlatform.OSXPlayer ||
                Application.platform == RuntimePlatform.OSXEditor)
            {
                string state = sdk.IsSdkInitialized ? "connected" : "scanning";
                return $"Devices: (macOS auto-connect target=\"{sdk.TargetDeviceName}\"; state={state})";
            }

            if (Application.platform != RuntimePlatform.WindowsPlayer &&
                Application.platform != RuntimePlatform.WindowsEditor)
            {
                return $"Devices: (scan list unavailable on {Application.platform})";
            }

            List<BLEApi.DeviceUpdate> devices = sdk.GetKnownDevicesSnapshot();
            if (devices.Count == 0)
                return "Devices: (scanning...)";

            var result = new StringBuilder();
            result.AppendLine($"Devices ({devices.Count}):");
            int shown = 0;
            foreach (BLEApi.DeviceUpdate device in devices)
            {
                if (shown++ >= 10)
                {
                    result.AppendLine("…");
                    break;
                }

                result.AppendLine(
                    $"- {device.name} [{(device.isConnectable ? "connectable" : "no")}] id={device.id}"
                );
            }

            return result.ToString();
        }

        private static string BuildSignalsText()
        {
            AidlabDelegate live = AidlabSDK.aidlabDelegate;
            AidlabSyncDelegate sync = AidlabSDK.aidlabSyncDelegate;
            var result = new StringBuilder();
            result.AppendLine("Signals (latest):");
            result.AppendLine($"Temp: {live.temperature.value:0.00} °C");
            result.AppendLine($"Wear: {live.wearState.value}");
            result.AppendLine($"HR: {live.heartRate.value} bpm");
            result.AppendLine($"RR: {live.rr.value} ms");
            result.AppendLine($"Resp: {live.respiration.value:0.000}");
            result.AppendLine($"EDA: {live.eda.value:0.000} µS");
            result.AppendLine($"Signal quality: {live.signalQuality.value}");
            result.AppendLine($"Sync state: {sync.syncState.value}");
            result.AppendLine(
                $"Unsynced: {sync.unsynchronizedSize.a} bytes, {sync.unsynchronizedSize.b:0.0} B/s"
            );
            return result.ToString();
        }

        private void EnsureStyles()
        {
            if (panelStyle != null)
                return;

            panelStyle = new GUIStyle(GUI.skin.box)
            {
                padding = new RectOffset(12, 12, 12, 12),
                alignment = TextAnchor.UpperLeft
            };
            headerStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = 18,
                fontStyle = FontStyle.Bold,
                wordWrap = true
            };
            bodyStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = 14,
                wordWrap = true
            };
        }

        private static string FormatSignals(Signal[] signals)
        {
            return signals == null || signals.Length == 0
                ? "(none)"
                : string.Join(", ", signals);
        }

        private static bool SignalsEqual(Signal[] left, Signal[] right)
        {
            if (ReferenceEquals(left, right))
                return true;
            if (left == null || right == null || left.Length != right.Length)
                return false;

            for (int index = 0; index < left.Length; index++)
            {
                if (left[index] != right[index])
                    return false;
            }

            return true;
        }
    }
}
