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

namespace Alibabacloud.Rum.Unity.WebGL
{
    public class WebGLRum : IAlibabacloudRum
    {
        private readonly AlibabacloudOptions _options;
        
        // Global view state tracking
        private string _currentViewId;
        private string _currentViewName;
        
        #region DllImport Declarations
        
        [DllImport("__Internal")]
        private static extern void AlibabacloudRum_SendEvent(string eventType, string eventDataJson);
        
        #endregion
        
        public WebGLRum(AlibabacloudOptions options)
        {
            _options = options;
        }
        
        public void ReportException(Exception ex)
        {
            if (ex == null)
            {
                return;
            }
            
            try
            {
                var eventData = new Dictionary<string, object>
                {
                    ["type"] = "crash",
                    ["name"] = ex.GetType().Name,
                    ["message"] = ex.Message ?? "No message",
                    ["stack"] = ex.StackTrace ?? "No stack trace",
                    ["source"] = "unity",
                    ["subtype"] = "unity",
                    ["view_id"] = _currentViewId ?? string.Empty,
                    ["view_name"] = _currentViewName ?? string.Empty
                };
                
                string eventDataJson = AttributesToJson(eventData);
                AlibabacloudRum_SendEvent("exception", eventDataJson);
            }
            catch (Exception e)
            {
                _options?.LogError("[ALIBABACLOUD] 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)
        {
            try
            {
                var eventData = new Dictionary<string, object>
                {
                    ["type"] = type ?? "xhr",
                    ["name"] = url ?? string.Empty,
                    ["method"] = method ?? "GET",
                    ["status_code"] = statusCode,
                    ["success"] = success,
                    ["view_id"] = _currentViewId ?? string.Empty,
                    ["view_name"] = _currentViewName ?? string.Empty
                };
                
                // Add duration if available
                if (measuring != null)
                {
                    eventData["duration"] = measuring.Duration;
                    eventData["size"] = measuring.Size;
                }
                
                // Add error message if failed
                if (!success && !string.IsNullOrEmpty(errorMessage))
                {
                    eventData["error_message"] = errorMessage;
                }
                
                // Add trace context if available
                if (traceContext != null)
                {
                    eventData["trace_id"] = traceContext.TraceId;
                    eventData["span_id"] = traceContext.SpanId;
                }
                
                string eventDataJson = AttributesToJson(eventData);
                AlibabacloudRum_SendEvent("resource", eventDataJson);
            }
            catch (Exception e)
            {
                _options?.LogError("ReportResource failed: {0}\n{1}", e.Message, e.StackTrace);
            }
        }

        public void ReportEvent(string eventName, string group, double value,
            Dictionary<string, object> info)
        {
            try
            {
                var eventData = new Dictionary<string, object>
                {
                    ["name"] = eventName ?? string.Empty,
                    ["group"] = group ?? string.Empty,
                    ["value"] = value,
                    ["view_id"] = _currentViewId ?? string.Empty,
                    ["view_name"] = _currentViewName ?? string.Empty
                };
                
                // Merge extra info into event data
                if (info != null)
                {
                    foreach (var kv in info)
                    {
                        if (!eventData.ContainsKey(kv.Key))
                        {
                            eventData[kv.Key] = kv.Value;
                        }
                    }
                }
                
                string eventDataJson = AttributesToJson(eventData);
                AlibabacloudRum_SendEvent("event", eventDataJson);
            }
            catch (Exception e)
            {
                _options?.LogError("ReportEvent failed: {0}\n{1}", e.Message, e.StackTrace);
            }
        }

        public void ReportLog(string logContent, string logName, string logLevel, string stackTrace, 
            Dictionary<string, object> extraInfo)
        {
            try
            {
                var eventData = new Dictionary<string, object>
                {
                    ["type"] = "log",
                    ["content"] = logContent ?? string.Empty,
                    ["level"] = logLevel ?? "info",
                    ["name"] = logName ?? "unity",
                    ["view_id"] = _currentViewId ?? string.Empty,
                    ["view_name"] = _currentViewName ?? string.Empty
                };
                
                if (!string.IsNullOrEmpty(stackTrace))
                {
                    eventData["stack_trace"] = stackTrace;
                }
                
                // Merge extra info into event data
                if (extraInfo != null)
                {
                    foreach (var kv in extraInfo)
                    {
                        if (!eventData.ContainsKey(kv.Key))
                        {
                            eventData[kv.Key] = kv.Value;
                        }
                    }
                }
                
                string eventDataJson = AttributesToJson(eventData);
                AlibabacloudRum_SendEvent("log", eventDataJson);
            }
            catch (Exception e)
            {
                _options?.LogError("ReportLog failed: {0}\n{1}", e.Message, e.StackTrace);
            }
        }

        public void StartView(string key, string name, Dictionary<string, object> attributes)
        {
            try
            {
                string viewName = !string.IsNullOrEmpty(name) ? name : key;
                
                // Generate 32-character hexadecimal random viewId
                string viewId = GenerateViewId();
                
                // Update global view state
                _currentViewId = viewId;
                _currentViewName = viewName;
                
                var eventData = new Dictionary<string, object>
                {
                    ["type"] = "view",
                    ["id"] = viewId,
                    ["url"] = key,
                    ["name"] = viewName,
                    ["view_type"] = "unity",
                    ["view_id"] = viewId,
                    ["view_name"] = viewName
                };
                
                // Merge attributes into event data
                if (attributes != null)
                {
                    foreach (var kv in attributes)
                    {
                        if (!eventData.ContainsKey(kv.Key))
                        {
                            eventData[kv.Key] = kv.Value;
                        }
                    }
                }
                
                string eventDataJson = AttributesToJson(eventData);
                AlibabacloudRum_SendEvent("view", eventDataJson);
            }
            catch (Exception e)
            {
                _options?.LogError("StartView failed: {0}\n{1}", e.Message, e.StackTrace);
            }
        }

        public void StopView(string key, Dictionary<string, object> attributes)
        {
            // Note: We don't immediately clear the view state here
            // because other events might still be reported for this view
            // The view state will be updated when StartView is called for the next view
        }
        
        /// <summary>
        /// Generate a 32-character hexadecimal random view ID
        /// </summary>
        private static string GenerateViewId()
        {
            var sb = new StringBuilder(32);
            var random = new System.Random();
            const string hexChars = "0123456789abcdef";
            
            for (int i = 0; i < 32; i++)
            {
                sb.Append(hexChars[random.Next(hexChars.Length)]);
            }
            
            return sb.ToString();
        }
        
        #region JSON Serialization Helpers
        
        private static string AttributesToJson(Dictionary<string, object> attributes)
        {
            if (attributes == null || attributes.Count == 0)
            {
                return "{}";
            }

            var sb = new StringBuilder();
            sb.Append("{");
            bool first = true;
            foreach (var kv in attributes)
            {
                // Skip internal attributes
                if (kv.Key.StartsWith("_alibabacloud."))
                {
                    continue;
                }
                
                if (!first) sb.Append(",");
                first = false;
                sb.Append($"\"{EscapeJson(kv.Key)}\":");
                
                if (kv.Value == null)
                {
                    sb.Append("null");
                }
                else if (kv.Value is string s)
                {
                    sb.Append($"\"{EscapeJson(s)}\"");
                }
                else if (kv.Value is bool b)
                {
                    sb.Append(b ? "true" : "false");
                }
                else if (kv.Value is long l)
                {
                    sb.Append(l);
                }
                else if (kv.Value is int i)
                {
                    sb.Append(i);
                }
                else if (kv.Value is float f)
                {
                    sb.Append(f);
                }
                else if (kv.Value is double d)
                {
                    sb.Append(d);
                }
                else
                {
                    sb.Append($"\"{EscapeJson(kv.Value.ToString() ?? "")}\"");
                }
            }
            sb.Append("}");
            return sb.ToString();
        }

        private static string EscapeJson(string s)
        {
            if (string.IsNullOrEmpty(s))
            {
                return s;
            }
            return s.Replace("\\", "\\\\")
                    .Replace("\"", "\\\"")
                    .Replace("\n", "\\n")
                    .Replace("\r", "\\r")
                    .Replace("\t", "\\t");
        }
        
        #endregion
    }
}