using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Alibabacloud.Rum.Unity.Logs;
using Alibabacloud.Rum.Unity.Network;
using Alibabacloud.Rum.Unity.Rum;
using UnityEngine;

namespace Alibabacloud.Rum.Unity.iOS
{
    public class IOSRum : IAlibabacloudRum
    {
        private readonly IOSPlatform _iosPlatform;
        private AlibabacloudOptions _options;
        
        // P/Invoke declarations for RUM reporting methods
        #region P/Invoke Declarations
        
        [DllImport("__Internal")]
        private static extern void AlibabaCloudRUMSDK_reportException(
            string exceptionType, 
            string causeBy, 
            string errorDump,
            string language);
        
        [DllImport("__Internal")]
        private static extern bool AlibabaCloudRUMSDK_reportCustomResource(
            string type,
            bool success,
            string url,
            string method,
            int statusCode,
            string errorMessage,
            string provider,
            IntPtr tracing,  // AlibabaCloudTraceContext pointer
            IntPtr measuring); // AlibabaCloudResourceMeasuring pointer
        
        [DllImport("__Internal")]
        private static extern void AlibabaCloudRUMSDK_setCustomEvent(
            string name,
            string type,
            string group,
            string snapshots,
            double value,
            IntPtr info); // NSDictionary pointer
        
        [DllImport("__Internal")]
        private static extern void AlibabaCloudRUMSDK_setCustomLog(
            string logInfo,
            string name,
            string type,
            string snapshots,
            string level,
            IntPtr info); // NSDictionary pointer
        
        // Helper methods for creating native objects
        [DllImport("__Internal")]
        private static extern IntPtr AlibabaCloudTraceContext_create(
            string traceId,
            string spanId,
            string parentSpanId,
            bool sampled,
            int protocol); // 0 = W3C, 1 = SkywalkingV3
        
        [DllImport("__Internal")]
        private static extern void AlibabaCloudTraceContext_release(IntPtr context);
        
        [DllImport("__Internal")]
        private static extern IntPtr AlibabaCloudResourceMeasuring_create(
            ulong duration,
            ulong size,
            ulong connectDuration,
            ulong sslDuration,
            ulong dnsDuration,
            ulong redirectDuration,
            ulong firstByteDuration,
            ulong downloadDuration);
        
        [DllImport("__Internal")]
        private static extern void AlibabaCloudResourceMeasuring_release(IntPtr measuring);
        
        [DllImport("__Internal")]
        private static extern IntPtr NSDictionary_create();
        
        [DllImport("__Internal")]
        private static extern void NSDictionary_addString(IntPtr dict, string key, string value);
        
        [DllImport("__Internal")]
        private static extern void NSDictionary_release(IntPtr dict);
        
        [DllImport("__Internal")]
        private static extern void AlibabaCloudRUMSDK_reportView(
            string viewId,
            int loadTime,
            bool enter,
            string viewName,
            string method,
            IntPtr map, // NSDictionary pointer
            string type);
        
        #endregion

        public IOSRum(IOSPlatform iosPlatform)
        {
            _iosPlatform = iosPlatform;
            _options = AlibabacloudSdk.Instance?.Options;
        }

        public void ReportException(Exception ex)
        {
            if (ex == null)
            {
                return;
            }
            
            try
            {
                string exceptionType = ex.GetType().Name;
                string causeBy = ex.Message ?? "No message";
                string errorDump = ex.StackTrace ?? "No stack trace";
                
                // Call iOS native method
                AlibabaCloudRUMSDK_reportException(exceptionType, causeBy, errorDump, AlibabacloudCommonParam.ScreenType);
            }
            catch (Exception e)
            {
                _options?.LogError("ReportException failed: {0}\n{1}", e.Message, e.StackTrace);
            }
        }

        public void ReportResource(string type,
            string url,
            string method,
            int statusCode,
            string errorMessage,
            bool success,
            TraceContext traceContext,
            Measuring measuring)
        {
            IntPtr traceContextPtr = IntPtr.Zero;
            IntPtr measuringPtr = IntPtr.Zero;
            
            try
            {
                // Create TraceContext native object
                if (traceContext != null)
                {
                    int protocol = traceContext.Protocol == TraceContextStandard.W3C ? 0 : 1;
                    
                    // SkyWalking protocol requires spanId to be a numeric string (long).
                    // Unity generates hex spanId (e.g. "b981687dbc054429"), which iOS SDK rejects.
                    // Convert hex to long decimal string for SkyWalking compatibility.
                    string spanIdForNative = traceContext.SpanId ?? string.Empty;
                    string parentSpanIdForNative = traceContext.ParentSpanId ?? string.Empty;
                    if (traceContext.Protocol == TraceContextStandard.SkyWalking)
                    {
                        spanIdForNative = HexToDecimalString(spanIdForNative);
                        if (!string.IsNullOrEmpty(parentSpanIdForNative))
                        {
                            parentSpanIdForNative = HexToDecimalString(parentSpanIdForNative);
                        }
                    }
                    
                    traceContextPtr = AlibabaCloudTraceContext_create(
                        traceContext.TraceId ?? string.Empty,
                        spanIdForNative,
                        parentSpanIdForNative,
                        traceContext.Sampled == 1,
                        protocol);
                }
                else
                {
                    // Create default trace context
                    traceContextPtr = AlibabaCloudTraceContext_create(
                        Guid.NewGuid().ToString("N"),
                        Guid.NewGuid().ToString("N").Substring(0, 16),
                        string.Empty,
                        true,
                        0); // W3C
                }
                
                // Create Measuring native object
                if (measuring != null)
                {
                    measuringPtr = AlibabaCloudResourceMeasuring_create(
                        (ulong)measuring.Duration,
                        (ulong)measuring.Size,
                        (ulong)measuring.ConnectDuration,
                        (ulong)measuring.SslDuration,
                        (ulong)measuring.DnsDuration,
                        (ulong)measuring.RedirectDuration,
                        (ulong)measuring.FirstByteDuration,
                        (ulong)measuring.DownloadDuration);
                }
                else
                {
                    // Create default measuring
                    measuringPtr = AlibabaCloudResourceMeasuring_create(0, 0, 0, 0, 0, 0, 0, 0);
                }
                
                // Call iOS native method
                AlibabaCloudRUMSDK_reportCustomResource(
                    type ?? "xhr",
                    success,
                    url ?? string.Empty,
                    method ?? "GET",
                    statusCode,
                    errorMessage ?? string.Empty,
                    "Unity",
                    traceContextPtr,
                    measuringPtr);
            }
            catch (Exception e)
            {
                _options?.LogError("ReportResource failed: {0}\n{1}", e.Message, e.StackTrace);
            }
            finally
            {
                // Release native objects
                if (traceContextPtr != IntPtr.Zero)
                {
                    AlibabaCloudTraceContext_release(traceContextPtr);
                }
                if (measuringPtr != IntPtr.Zero)
                {
                    AlibabaCloudResourceMeasuring_release(measuringPtr);
                }
            }
        }

        public void ReportEvent(string eventName, string group, double value,
            Dictionary<string, object> info)
        {
            IntPtr dictPtr = IntPtr.Zero;
            
            try
            {
                // Create NSDictionary for info
                if (info != null && info.Count > 0)
                {
                    dictPtr = NSDictionary_create();
                    foreach (var kv in info)
                    {
                        // Convert value to string for NSDictionary
                        string valueStr = kv.Value?.ToString() ?? string.Empty;
                        NSDictionary_addString(dictPtr, kv.Key, valueStr);
                    }
                }
                
                // Call iOS native method
                AlibabaCloudRUMSDK_setCustomEvent(
                    eventName ?? string.Empty,
                    null, // type - not provided in interface
                    group ?? string.Empty,
                    null, // snapshots - not provided in interface
                    value,
                    dictPtr);
            }
            catch (Exception e)
            {
                _options?.LogError("ReportEvent failed: {0}\n{1}", e.Message, e.StackTrace);
            }
            finally
            {
                if (dictPtr != IntPtr.Zero)
                {
                    NSDictionary_release(dictPtr);
                }
            }
        }

        public void ReportLog(string logContent, string logName, string logLevel, string stackTrace, Dictionary<string, object> extraInfo)
        {
            IntPtr dictPtr = IntPtr.Zero;
            
            try
            {
                // Create NSDictionary for extraInfo
                if (extraInfo != null && extraInfo.Count > 0)
                {
                    dictPtr = NSDictionary_create();
                    foreach (var kv in extraInfo)
                    {
                        // Convert value to string for NSDictionary
                        string valueStr = kv.Value?.ToString() ?? string.Empty;
                        NSDictionary_addString(dictPtr, kv.Key, valueStr);
                    }
                }
                
                // Call iOS native method
                AlibabaCloudRUMSDK_setCustomLog(
                    logContent ?? string.Empty,
                    logName ?? string.Empty,
                    null, // type - not provided in interface
                    stackTrace ?? string.Empty, // Use stackTrace as snapshots
                    logLevel ?? "Info",
                    dictPtr);
            }
            catch (Exception)
            {
                // Silently ignore P/Invoke errors
            }
            finally
            {
                if (dictPtr != IntPtr.Zero)
                {
                    NSDictionary_release(dictPtr);
                }
            }
        }

        public void StartView(string key, string name, Dictionary<string, object> attributes)
        {
            IntPtr dictPtr = IntPtr.Zero;
            
            try
            {
                // Extract loadTime from attributes if present
                int loadTime = 0;
                if (attributes != null && attributes.ContainsKey("_alibabacloud.load_time"))
                {
                    if (attributes["_alibabacloud.load_time"] is long loadTimeMs)
                    {
                        loadTime = (int)loadTimeMs;
                    }
                }
                
                // Create NSDictionary for attributes (excluding internal ones)
                if (attributes != null && attributes.Count > 0)
                {
                    dictPtr = NSDictionary_create();
                    foreach (var kv in attributes)
                    {
                        // Skip internal attributes
                        if (kv.Key.StartsWith("_alibabacloud."))
                        {
                            continue;
                        }
                        // Convert value to string for NSDictionary
                        string valueStr = kv.Value?.ToString() ?? string.Empty;
                        NSDictionary_addString(dictPtr, kv.Key, valueStr);
                    }
                }

                // Call iOS native method
                AlibabaCloudRUMSDK_reportView(
                    key ?? string.Empty,
                    loadTime,
                    true,  // enter = true for StartView
                    name ?? string.Empty,
                    AlibabacloudCommonParam.ScreenType,  // method parameter
                    dictPtr,
                    AlibabacloudCommonParam.ScreenType); // type parameter
            }
            catch (Exception e)
            {
                _options?.LogError("StartView failed: {0}\n{1}", e.Message, e.StackTrace);
            }
            finally
            {
                if (dictPtr != IntPtr.Zero)
                {
                    NSDictionary_release(dictPtr);
                }
            }
        }

        /// <summary>
        /// Converts a hex string to a decimal long string for SkyWalking SpanId compatibility.
        /// SkyWalking protocol requires spanId to be a numeric string.
        /// </summary>
        private static string HexToDecimalString(string hex)
        {
            if (string.IsNullOrEmpty(hex)) return "0";
            try
            {
                // Parse as ulong to handle full 64-bit hex values, then reinterpret as long
                ulong value = Convert.ToUInt64(hex, 16);
                long signed = (long)value;
                return signed.ToString();
            }
            catch
            {
                return "0";
            }
        }

        public void StopView(string key, Dictionary<string, object> attributes)
        {
            IntPtr dictPtr = IntPtr.Zero;
            
            try
            {
                // Extract loadTime from attributes if present
                int loadTime = 0;
                if (attributes != null && attributes.ContainsKey("_alibabacloud.load_time"))
                {
                    if (attributes["_alibabacloud.load_time"] is long loadTimeMs)
                    {
                        loadTime = (int)loadTimeMs;
                    }
                }

                // Create NSDictionary for attributes (excluding internal ones)
                if (attributes != null && attributes.Count > 0)
                {
                    dictPtr = NSDictionary_create();
                    foreach (var kv in attributes)
                    {
                        // Skip internal attributes
                        if (kv.Key.StartsWith("_alibabacloud."))
                        {
                            continue;
                        }
                        // Convert value to string for NSDictionary
                        string valueStr = kv.Value?.ToString() ?? string.Empty;
                        NSDictionary_addString(dictPtr, kv.Key, valueStr);
                    }
                }

                // Call iOS native method
                AlibabaCloudRUMSDK_reportView(
                    key ?? string.Empty,
                    loadTime,
                    false,  // enter = false for StopView
                    string.Empty,  // name not needed for stop
                    AlibabacloudCommonParam.ScreenType,  // method parameter
                    dictPtr,
                    AlibabacloudCommonParam.ScreenType); // type parameter
            }
            catch (Exception e)
            {
                _options?.LogError("StopView failed: {0}\n{1}", e.Message, e.StackTrace);
            }
            finally
            {
                if (dictPtr != IntPtr.Zero)
                {
                    NSDictionary_release(dictPtr);
                }
            }
        }
    }
}