using System;
using System.Collections.Generic;
using Alibabacloud.Rum.Unity;
using Alibabacloud.Rum.Unity.Rum;

namespace Alibabacloud.Rum.Unity.Network
{
    /// <summary>
    /// Manages trace context propagation for distributed tracing.
    /// </summary>
    public static class TraceContextManager
    {
        private static string _currentTraceId;
        private static string _currentSpanId;
        private static readonly object _lock = new object();

        /// <summary>
        /// Initialize or update the current trace context.
        /// </summary>
        public static void InitializeContext(string traceId = null, string spanId = null)
        {
            lock (_lock)
            {
                _currentTraceId = traceId ?? GenerateTraceId();
                _currentSpanId = spanId ?? GenerateSpanId();
            }
        }

        /// <summary>
        /// Get current trace ID.
        /// </summary>
        public static string GetTraceId()
        {
            lock (_lock)
            {
                if (string.IsNullOrEmpty(_currentTraceId))
                {
                    _currentTraceId = GenerateTraceId();
                }
                return _currentTraceId;
            }
        }

        /// <summary>
        /// Generate a new span ID for the current request.
        /// </summary>
        public static string GenerateNewSpanId()
        {
            return GenerateSpanId();
        }

        /// <summary>
        /// Check if URL should have trace context injected and return the matching config.
        /// </summary>
        public static TraceContextConfig GetMatchingConfig(AlibabacloudOptions options, string url)
        {
            if (!options.EnableTraceContext || string.IsNullOrEmpty(url))
            {
                return null;
            }

            if (options.TraceContextConfigs == null || options.TraceContextConfigs.Length == 0)
            {
                return null;
            }

            // Find the first matching pattern
            foreach (var config in options.TraceContextConfigs)
            {
                if (config != null && !string.IsNullOrEmpty(config.UrlPattern) && 
                    url.IndexOf(config.UrlPattern, StringComparison.Ordinal) >= 0)
                {
                    return config;
                }
            }

            return null;
        }

        /// <summary>
        /// Check if URL should have trace context injected based on patterns.
        /// </summary>
        public static bool ShouldInjectTraceContext(AlibabacloudOptions options, string url)
        {
            return GetMatchingConfig(options, url) != null;
        }

        /// <summary>
        /// Inject trace context headers based on URL and its matching configuration.
        /// </summary>
        public static Dictionary<string, string> GetTraceHeaders(AlibabacloudOptions options, string url)
        {
            var headers = new Dictionary<string, string>();

            if (!options.EnableTraceContext)
            {
                return headers;
            }

            var config = GetMatchingConfig(options, url);
            if (config == null)
            {
                return headers;
            }

            var traceId = GetTraceId();
            var spanId = GenerateNewSpanId();

            switch (config.Standard)
            {
                case TraceContextStandard.W3C:
                    InjectW3CHeaders(headers, traceId, spanId);
                    break;
                case TraceContextStandard.SkyWalking:
                    InjectSkyWalkingHeaders(headers, traceId, spanId);
                    break;
            }

            return headers;
        }

        /// <summary>
        /// Inject W3C Trace Context headers.
        /// Format: traceparent: 00-{trace-id}-{parent-id}-{trace-flags}
        /// </summary>
        private static void InjectW3CHeaders(Dictionary<string, string> headers, string traceId, string spanId)
        {
            // W3C traceparent format: version-trace-id-parent-id-trace-flags
            // version: 00 (current version)
            // trace-id: 32 hex characters (16 bytes)
            // parent-id: 16 hex characters (8 bytes)
            // trace-flags: 2 hex characters (1 byte), 01 = sampled
            var traceparent = $"00-{traceId}-{spanId}-01";
            headers["traceparent"] = traceparent;

            // Optional tracestate header for vendor-specific data
            // headers["tracestate"] = ""; // Can be extended if needed
        }

        /// <summary>
        /// Inject SkyWalking trace context header.
        /// Format: sw8: 1-{trace-id}-{segment-id}-{span-id}-{service}-{service-instance}-{endpoint}-{peer}
        /// </summary>
        private static void InjectSkyWalkingHeaders(Dictionary<string, string> headers, string traceId, string spanId)
        {
            // SkyWalking sw8 format (base64 encoded)
            // Sample version: 1 (version)
            var service = "unity-app";
            var serviceInstance = GetServiceInstance();
            var endpoint = "-"; // Will be set to actual endpoint/URL
            var peer = "-"; // Target address

            // Simplified format: version-traceId-segmentId-spanId-service-serviceInstance-endpoint-peer
            var sw8Value = $"1-{traceId}-{spanId}-0-{Base64Encode(service)}-{Base64Encode(serviceInstance)}-{Base64Encode(endpoint)}-{Base64Encode(peer)}";
            headers["sw8"] = sw8Value;
        }

        private static string GenerateTraceId()
        {
            // W3C: 32 hex characters (128 bits / 16 bytes)
            // Generate using Guid + timestamp for better uniqueness
            var guid = Guid.NewGuid().ToString("N"); // 32 hex chars
            return guid;
        }

        private static string GenerateSpanId()
        {
            // W3C parent-id: 16 hex characters (64 bits / 8 bytes)
            var guid = Guid.NewGuid().ToString("N");
            return guid.Substring(0, 16); // Take first 16 characters
        }

        private static string GetServiceInstance()
        {
            // Use a combination of device ID and timestamp
            return $"{UnityEngine.SystemInfo.deviceUniqueIdentifier.GetHashCode():X8}";
        }

        private static string Base64Encode(string plainText)
        {
            if (string.IsNullOrEmpty(plainText))
            {
                return string.Empty;
            }
            var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
            return Convert.ToBase64String(plainTextBytes);
        }
    }
}
