using System;
using System.Collections.Generic;
using System.Diagnostics;
using Alibabacloud.Rum.Unity;
using Alibabacloud.Rum.Unity.Logs;
using Alibabacloud.Rum.Unity.Rum;
using UnityEngine.Networking;

namespace Alibabacloud.Rum.Unity.Network
{
    /**
     * todo 1. 日志打印统一一下
     * 2. w3c/skywalking 转不出来，导致发不出去
     * 3. 代码记得简化一下，日志也删一下
     */
    public static class InstrumentedWebRequest
    {
        private const string DefaultResourceType = "xhr";

        public static UnityWebRequest CreateInstrumentedRequest(string url, string method)
        {
            return new UnityWebRequest(url, method);
        }

        public static UnityWebRequestAsyncOperation SendWebRequest(UnityWebRequest request)
        {
         

            var sdk = AlibabacloudSdk.Instance;
            var options = sdk.Options;
            if (options == null || !options.EnableNetworkTracking)
            {
                return request.SendWebRequest();
            }
            if (!ShouldTrackRequest(options, request.url, request.method))
            {
                return request.SendWebRequest();
            }

            // Prepare trace context before request
            TraceContext traceContext = null;
            TraceContextConfig traceConfig = null;
            if (options.EnableTraceContext)
            {
                traceConfig = TraceContextManager.GetMatchingConfig(options, request.url);
                if (traceConfig != null)
                {
                    var traceId = TraceContextManager.GetTraceId();
                    var spanId = TraceContextManager.GenerateNewSpanId();
                    var protocol = traceConfig.Standard == TraceContextStandard.W3C 
                        ? TraceContextStandard.W3C 
                        : TraceContextStandard.SkyWalking;
                    traceContext = new TraceContext(traceId, spanId, protocol);

                    // Inject trace context headers
                    var traceHeaders = TraceContextManager.GetTraceHeaders(options, request.url);
                    foreach (var header in traceHeaders)
                    {
                        request.SetRequestHeader(header.Key, header.Value);
                    }
                }
            }
            
            var rum = sdk.Rum;

            var httpMethod = MapHttpMethod(request.method);
            var attributes = BuildStartAttributes(options, request);


            // Start timing
            var stopwatch = Stopwatch.StartNew();
            var url = request.url;
            var method = request.method;

            var operation = request.SendWebRequest();
            operation.completed += _ =>
            {
                HandleCompletedRequest(rum, options, request, url, method, attributes, traceContext, stopwatch);
            };

            return operation;
        }

        private static void HandleCompletedRequest(IAlibabacloudRum rum, AlibabacloudOptions options,
            UnityWebRequest request, string url, string method, Dictionary<string, object> attributes, TraceContext traceContext, Stopwatch stopwatch)
        {
            stopwatch.Stop();
            
            int statusCode = 0;
            try
            {
                if (request != null && request.responseCode > 0)
                {
                    statusCode = (int)request.responseCode;
                }
            }
            catch (Exception exception)
            {
                options.LogWarning("InstrumentedWebRequest: GetResponseCode failed, {0}", exception.Message);
                // UnityWebRequest might be disposed or throw if accessed at a bad time
            }

            // Status code range filter
            if (statusCode > 0 && !ShouldTrackStatusCode(options, statusCode))
            {
                return;
            }

            // Add response headers if configured
            try
            {
                if (options.CaptureResponseHeaders && request != null && request.GetResponseHeaders() != null)
                {
                    var responseHeaders = CaptureHeaders(
                        request.GetResponseHeaders(),
                        options.NetworkResponseHeaderAllowlist,
                        options.NetworkResponseHeaderBlocklist);
                    foreach (var header in responseHeaders)
                    {
                        attributes[$"network.response_header.{header.Key}"] = header.Value;
                    }
                }
            }
            catch
            {
                // request might be disposed
            }

            long responseSize = 0L;
            try
            {
                if (options.CaptureResponseBodySize && request != null)
                {
                    responseSize = (long)request.downloadedBytes;
                }
            }
            catch
            {
                // request might be disposed
            }

            bool success = false;
            string errorMessage = string.Empty;

            try
            {
                if (request != null)
                {
                    success = request.result == UnityWebRequest.Result.Success;

                    if (request.result != UnityWebRequest.Result.Success)
                    {
                        errorMessage = request.error ?? string.Empty;
                        var errorInfo = new ErrorInfo("UnityWebRequestError", errorMessage);
                    }
                }
            }
            catch
            {
                // request might be disposed
            }

            // Report resource with detailed metrics
            try
            {
                var measuring = new Measuring(
                    duration: stopwatch.ElapsedMilliseconds,
                    size: responseSize);

                string reportMethod = method?.ToUpperInvariant() ?? "GET";

                rum.ReportResource(
                    type: "xhr",
                    url: url ?? string.Empty,
                    method: reportMethod,
                    statusCode: statusCode,
                    errorMessage: errorMessage,
                    success: success,
                    traceContext: traceContext,
                    measuring: measuring);
            }
            catch (Exception ex)
            {
                options.LogError(ex, "Failed to report resource: {0}", ex.Message);
            }
        }

        private static bool ShouldTrackStatusCode(AlibabacloudOptions options, int statusCode)
        {
            if (options.NetworkStatusCodeRanges == null || options.NetworkStatusCodeRanges.Length == 0)
            {
                return true;
            }

            foreach (var range in options.NetworkStatusCodeRanges)
            {
                if (string.IsNullOrEmpty(range))
                {
                    continue;
                }

                var parts = range.Split('-');
                if (parts.Length == 2)
                {
                    if (int.TryParse(parts[0].Trim(), out var min) && int.TryParse(parts[1].Trim(), out var max))
                    {
                        if (statusCode >= min && statusCode <= max)
                        {
                            return true;
                        }
                    }
                }
                else if (int.TryParse(range.Trim(), out var exactCode))
                {
                    if (statusCode == exactCode)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        private static Dictionary<string, string> CaptureHeaders(
            Dictionary<string, string> headers,
            string[] allowlist,
            string[] blocklist)
        {
            var result = new Dictionary<string, string>();

            if (headers == null)
            {
                return result;
            }

            foreach (var header in headers)
            {
                var headerName = header.Key.ToLowerInvariant();

                // Check blocklist
                if (blocklist != null && blocklist.Length > 0)
                {
                    var blocked = false;
                    foreach (var blockedHeader in blocklist)
                    {
                        if (!string.IsNullOrEmpty(blockedHeader) && 
                            headerName == blockedHeader.ToLowerInvariant().Trim())
                        {
                            blocked = true;
                            break;
                        }
                    }
                    if (blocked)
                    {
                        continue;
                    }
                }

                // Check allowlist
                if (allowlist != null && allowlist.Length > 0)
                {
                    var allowed = false;
                    foreach (var allowedHeader in allowlist)
                    {
                        if (!string.IsNullOrEmpty(allowedHeader) && 
                            headerName == allowedHeader.ToLowerInvariant().Trim())
                        {
                            allowed = true;
                            break;
                        }
                    }
                    if (!allowed)
                    {
                        continue;
                    }
                }

                result[header.Key] = header.Value;
            }

            return result;
        }

        private static bool ShouldTrackRequest(AlibabacloudOptions options, string url, string method = null)
        {
            if (!options.EnableNetworkTracking || string.IsNullOrEmpty(url))
            {
                return false;
            }

            // HTTP Method filter
            if (!string.IsNullOrEmpty(method) && HasHttpMethodFilters(options))
            {
                if (!IsHttpMethodAllowed(options, method))
                {
                    return false;
                }
            }

            // URL Blocklist
            if (options.NetworkUrlBlocklist != null)
            {
                foreach (var pattern in options.NetworkUrlBlocklist)
                {
                    if (!string.IsNullOrEmpty(pattern) && url.IndexOf(pattern, StringComparison.Ordinal) >= 0)
                    {
                        return false;
                    }
                }
            }

            // URL Allowlist
            var allowlist = options.NetworkUrlAllowlist;
            if (allowlist != null && allowlist.Length > 0)
            {
                var matchedAllowlist = false;
                foreach (var pattern in allowlist)
                {
                    if (!string.IsNullOrEmpty(pattern) && url.IndexOf(pattern, StringComparison.Ordinal) >= 0)
                    {
                        matchedAllowlist = true;
                        break;
                    }
                }

                if (!matchedAllowlist)
                {
                    return false;
                }
            }

            // Sample Rate
            if (options.NetworkSampleRate <= 0)
            {
                return false;
            }

            if (options.NetworkSampleRate >= 1.0)
            {
                return true;
            }

            // Simple probabilistic sampling; in the future can be replaced with deterministic hashing
            var randomValue = UnityEngine.Random.value;
            return randomValue <= options.NetworkSampleRate;
        }

        private static bool HasHttpMethodFilters(AlibabacloudOptions options)
        {
            return options.TrackHttpGet || options.TrackHttpPost || options.TrackHttpPut ||
                   options.TrackHttpDelete || options.TrackHttpPatch || options.TrackHttpHead ||
                   options.TrackHttpOptions;
        }

        private static bool IsHttpMethodAllowed(AlibabacloudOptions options, string method)
        {
            if (string.IsNullOrEmpty(method))
            {
                return false;
            }

            var methodUpper = method.ToUpperInvariant();
            switch (methodUpper)
            {
                case "GET":
                    return options.TrackHttpGet;
                case "POST":
                    return options.TrackHttpPost;
                case "PUT":
                    return options.TrackHttpPut;
                case "DELETE":
                    return options.TrackHttpDelete;
                case "PATCH":
                    return options.TrackHttpPatch;
                case "HEAD":
                    return options.TrackHttpHead;
                case "OPTIONS":
                    return options.TrackHttpOptions;
                default:
                    return false;
            }
        }

        private static RumHttpMethod MapHttpMethod(string method)
        {
            if (string.IsNullOrEmpty(method))
            {
                return RumHttpMethod.Get;
            }

            switch (method.ToUpperInvariant())
            {
                case "POST":
                    return RumHttpMethod.Post;
                case "HEAD":
                    return RumHttpMethod.Head;
                case "PUT":
                    return RumHttpMethod.Put;
                case "DELETE":
                    return RumHttpMethod.Delete;
                case "PATCH":
                    return RumHttpMethod.Patch;
                default:
                    return RumHttpMethod.Get;
            }
        }

        private static string GenerateResourceKey(string url)
        {
            return string.IsNullOrEmpty(url)
                ? Guid.NewGuid().ToString()
                : $"{url}-{Guid.NewGuid():N}";
        }

        private static Dictionary<string, object> BuildStartAttributes(AlibabacloudOptions options, UnityWebRequest request)
        {
            var attributes = new Dictionary<string, object>
            {
                { "network.url", request.url },
                { "network.method", request.method },
            };

            // Capture request body size
            if (options.CaptureRequestBodySize && request.uploadHandler != null)
            {
                attributes["network.request_body_size"] = request.uploadHandler.data != null
                    ? request.uploadHandler.data.LongLength
                    : 0L;
            }

            // Capture request headers
            if (options.CaptureRequestHeaders)
            {
                var requestHeaders = new Dictionary<string, string>();
                try
                {
                    // UnityWebRequest doesn't provide direct access to all headers
                    // We can only access headers that were explicitly set
                    // Common headers that might be set:
                    var commonHeaders = new[] { "Content-Type", "Authorization", "User-Agent", "Accept" };
                    foreach (var headerName in commonHeaders)
                    {
                        var headerValue = request.GetRequestHeader(headerName);
                        if (!string.IsNullOrEmpty(headerValue))
                        {
                            requestHeaders[headerName] = headerValue;
                        }
                    }
                }
                catch
                {
                    // ignore failures to read request headers
                }

                var capturedHeaders = CaptureHeaders(
                    requestHeaders,
                    options.NetworkRequestHeaderAllowlist,
                    options.NetworkRequestHeaderBlocklist);
                
                foreach (var header in capturedHeaders)
                {
                    attributes[$"network.request_header.{header.Key}"] = header.Value;
                }
            }

            return attributes;
        }
    }
}
