using Best.HTTP;
using Best.HTTP.Response;
using Best.HTTP.Shared.PlatformSupport.Memory;
using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using UVC.Json;
using UVC.Log;

namespace UVC.Network
{
    /// <summary>
    /// HTTP 요청을 처리하기 위한 유틸리티 클래스
    /// </summary>
    /// <remarks>
    /// 이 클래스는 REST API 호출을 위한 메소드들을 제공합니다.
    /// GET, POST 요청 및 파일 다운로드를 지원하며 요청/응답 로깅 기능도 포함합니다.
    /// 요청은 비동기(UniTask)로 처리됩니다.
    /// 
    /// 기본 사용법:
    /// <code>
    /// // 도메인 설정
    /// HttpRequester.Domain = "https://api.example.com";
    /// 
    /// // GET 요청 예제
    /// var products = await HttpRequester.RequestGet<List<Product>>("/api/products");
    /// 
    /// // POST 요청 예제
    /// var loginData = new Dictionary<string, object>
    /// {
    ///     { "username", "user@example.com" },
    ///     { "password", "password123" }
    /// };
    /// var response = await HttpRequester.RequestPost<LoginResponse>("/api/login", loginData);
    /// </code>
    /// </remarks>
    public class HttpRequester
    {
        /// <summary>
        /// 기본 도메인 주소입니다.
        /// </summary>
        public static string Domain { get; set; } = "";

        /// <summary>
        /// Dictionary 형태의 본문을 가진 사용자 정의 HTTP 메소드로 API 요청을 수행합니다.
        /// </summary>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL (http가 포함되지 않은 경우 Domain과 결합)</param>
        /// <param name="method">HTTP 메소드 (get, post, put 등)</param>
        /// <param name="body">요청 본문으로 전송할 Dictionary 데이터 (null 가능)</param>
        /// <param name="header">추가할 헤더 정보 (null 가능)</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터</returns>
        /// <example>
        /// <code>
        /// // PUT 요청 예제
        /// public async UniTask<ProductResponse> UpdateProductAsync(int productId, string name, float price)
        /// {
        ///     var body = new Dictionary<string, object>
        ///     {
        ///         { "id", productId },
        ///         { "name", name },
        ///         { "price", price }
        ///     };
        ///     
        ///     return await HttpRequester.Request<ProductResponse>("/api/products", "put", body);
        /// }
        /// </code>
        /// </example>
        public static async UniTask<T> Request<T>(string url, string method, Dictionary<string, object> body = null, Dictionary<string, string> header = null, bool useAuth = false)
        {
            return await Request_<T>(url, body == null ? null : JsonHelper.ToJson(body), method, header, useAuth);
        }

        /// <summary>
        /// 문자열 형태의 본문을 가진 사용자 정의 HTTP 메소드로 API 요청을 수행합니다.
        /// </summary>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL (http가 포함되지 않은 경우 Domain과 결합)</param>
        /// <param name="method">HTTP 메소드 (get, post, put 등)</param>
        /// <param name="body">요청 본문으로 전송할 JSON 문자열 (null 가능)</param>
        /// <param name="header">추가할 헤더 정보 (null 가능)</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터</returns>
        /// <example>
        /// <code>
        /// // DELETE 요청 예제
        /// public async UniTask<bool> DeleteResourceAsync(string resourceId)
        /// {
        ///     string jsonBody = $"{{\"id\": \"{resourceId}\"}}";
        ///     
        ///     return await HttpRequester.Request<bool>("/api/resources", "delete", jsonBody);
        /// }
        /// </code>
        /// </example>
        public static async UniTask<T> Request<T>(string url, string method, string body = null, Dictionary<string, string> header = null, bool useAuth = false)
        {
            return await Request_<T>(url, body, method, header, useAuth);
        }


        /// <summary>
        /// Dictionary를 JSON body로 변환하여 POST 요청을 수행합니다.
        /// </summary>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL (http가 포함되지 않은 경우 Domain과 결합)</param>
        /// <param name="body">요청 본문으로 전송할 Dictionary 데이터 (null 가능)</param>
        /// <param name="header">추가할 헤더 정보 (null 가능)</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터</returns>
        /// <example>
        /// <code>
        /// // 사용자 로그인 요청 예제
        /// public async UniTask<LoginResponse> LoginAsync(string username, string password)
        /// {
        ///     var body = new Dictionary<string, object>
        ///     {
        ///         { "username", username },
        ///         { "password", password }
        ///     };
        ///     
        ///     return await HttpRequester.RequestPost<LoginResponse>("/api/login", body);
        /// }
        /// 
        /// // LoginResponse 클래스 예제
        /// [Serializable]
        /// public class LoginResponse
        /// {
        ///     public string token;
        ///     public UserInfo userInfo;
        /// }
        /// </code>
        /// </example>
        public static async UniTask<T> RequestPost<T>(string url, Dictionary<string, object> body = null, Dictionary<string, string> header = null, bool useAuth = false)
        {
            return await Request_<T>(url, body == null ? null : JsonHelper.ToJson(body), "post", header, useAuth);
        }

        /// <summary>
        /// 문자열 형태의 JSON을 body로 사용하여 POST 요청을 수행합니다.
        /// </summary>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL (http가 포함되지 않은 경우 Domain과 결합)</param>
        /// <param name="body">요청 본문으로 전송할 JSON 문자열</param>
        /// <param name="header">추가할 헤더 정보 (null 가능)</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터</returns>
        /// <example>
        /// <code>
        /// // 사용자 정의 JSON 문자열로 요청하는 예제
        /// public async UniTask<ProductResponse> UpdateProductAsync(int productId, string productName)
        /// {
        ///     string jsonBody = $"{{\"id\": {productId}, \"name\": \"{productName}\"}}";
        ///     
        ///     return await HttpRequester.RequestPost<ProductResponse>("/api/products/update", jsonBody);
        /// }
        /// </code>
        /// </example>
        public static async UniTask<T> RequestPost<T>(string url, string body, Dictionary<string, string> header = null, bool useAuth = false)
        {
            return await Request_<T>(url, body, "post", header, useAuth);
        }

        /// <summary>
        /// GET 방식으로 API를 호출합니다.
        /// </summary>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL (http가 포함되지 않은 경우 Domain과 결합)</param>
        /// <param name="body">요청에 포함할 파라미터 (URL 쿼리스트링으로 변환)</param>
        /// <param name="header">추가할 헤더 정보 (null 가능)</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터</returns>
        /// <example>
        /// <code>
        /// // 상품 목록을 가져오는 예제
        /// public async UniTask<List<Product>> GetProductsAsync(int page = 1, int pageSize = 20)
        /// {
        ///     var queryParams = new Dictionary<string, object>
        ///     {
        ///         { "page", page },
        ///         { "pageSize", pageSize }
        ///     };
        ///     
        ///     return await HttpRequester.RequestGet<List<Product>>("/api/products", queryParams);
        /// }
        /// 
        /// // Product 클래스 예제
        /// [Serializable]
        /// public class Product
        /// {
        ///     public int id;
        ///     public string name;
        ///     public float price;
        /// }
        /// </code>
        /// </example>
        public static async UniTask<T> RequestGet<T>(string url, Dictionary<string, object> body = null, Dictionary<string, string> header = null, bool useAuth = false)
        {
            return await Request_<T>(url, body == null ? null : JsonHelper.ToJson(body), "get", header, useAuth);
        }

        /// <summary>
        /// HTTP 요청을 처리하는 내부 메소드
        /// </summary>
        /// <remarks>
        /// 이 메소드는 모든 HTTP 요청의 공통 로직을 처리합니다:
        /// - URL 구성 (Domain과 상대 경로 결합)
        /// - HTTP 메소드 설정
        /// - 헤더 설정
        /// - 요청 로깅
        /// - 응답 처리 및 역직렬화
        /// </remarks>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL</param>
        /// <param name="body">요청 본문 (JSON 문자열)</param>
        /// <param name="methodString">HTTP 메소드 문자열</param>
        /// <param name="header">추가할 헤더 정보</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터</returns>
        private static async UniTask<T> Request_<T>(string url, string body = null, string methodString = "post", Dictionary<string, string> header = null, bool useAuth = false)
        {
            HTTPMethods method = StringToMethod(methodString);

            if (!url.Contains("http")) url = $"{Domain}{url}";

            var request = SelectHTTPRequest(method, url);

            request.DownloadSettings = new Best.HTTP.Request.Settings.DownloadSettings() { ContentStreamMaxBuffered = 1024 * 1024 * 200 };
            request.MethodType = method;
            request.SetHeader("Content-Type", "application/json; charset=utf-8");
            //if (useAuth) request.SetHeader("access-token", AuthService.Instance.Entiti.accessToken);
            JObject headerObject = new JObject();
            headerObject.Add("Content-Type", "application/json; charset=utf-8");

            if (header != null)
            {
                foreach (var kvp in header)
                {
                    request.SetHeader(kvp.Key, kvp.Value);
                    headerObject.Add(kvp.Key, kvp.Value);
                }
            }
            HttpLogEntry log = ServerLog.LogHttpRequest(url, methodString, headerObject.ToString(Formatting.None), body, DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
            //Debug.Log($"Request APIToken :{AuthService.Instance.Entiti.accessToken}");
            if (body != null)
            {
                request.UploadSettings.UploadStream =
                        new MemoryStream(Encoding.UTF8.GetBytes(body));
            }
            //var response = await request.GetFromJsonResultAsync<T>();
            var response = await request.GetAsStringAsync();
            log.ResponseData = response;
            log.ResponseDate = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            ServerLog.LogHttpResponse(log);
            //T가 string이면
            if (typeof(T) == typeof(string))
            {
                return (T)(object)response;
            }
            else
            {
                return JsonHelper.FromJson<T>(response);
            }
        }

        /// <summary>
        /// Dictionary를 JSON body로 변환하여 POST 요청을 수행하고 처리 시간도 함께 반환합니다.
        /// </summary>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL (http가 포함되지 않은 경우 Domain과 결합)</param>
        /// <param name="body">요청 본문으로 전송할 Dictionary 데이터 (null 가능)</param>
        /// <param name="header">추가할 헤더 정보 (null 가능)</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터와 요청 처리 시간</returns>
        /// <example>
        /// <code>
        /// // 서버 응답 시간을 측정하는 예제
        /// public async UniTask<(UserData, TimeSpan)> GetUserDataWithPerformanceCheck(int userId)
        /// {
        ///     var body = new Dictionary<string, object>
        ///     {
        ///         { "userId", userId }
        ///     };
        ///     
        ///     var (userData, duration) = await HttpRequester.RequestPostWithDuration<UserData>("/api/users/details", body);
        ///     
        ///     Debug.Log($"API 응답 시간: {duration.TotalMilliseconds}ms");
        ///     return (userData, duration);
        /// }
        /// </code>
        /// </example>
        public static async UniTask<(T, TimeSpan)> RequestPostWithDuration<T>(string url, Dictionary<string, object> body = null, Dictionary<string, string> header = null, bool useAuth = true)
        {
            return await RequestWithDuration<T>(url, body == null ? null : JsonHelper.ToJson(body), "post", header, useAuth);
        }

        /// <summary>
        /// 문자열 형태의 JSON을 body로 사용하여 POST 요청을 수행하고 처리 시간도 함께 반환합니다.
        /// </summary>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL (http가 포함되지 않은 경우 Domain과 결합)</param>
        /// <param name="body">요청 본문으로 전송할 JSON 문자열</param>
        /// <param name="header">추가할 헤더 정보 (null 가능)</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터와 요청 처리 시간</returns>
        /// <example>
        /// <code>
        /// // 커스텀 JSON으로 요청하고 응답 시간을 측정하는 예제
        /// public async UniTask<(APIResponse, TimeSpan)> SendComplexDataWithTiming(string jsonData)
        /// {
        ///     // jsonData는 이미 직렬화된 JSON 문자열
        ///     
        ///     var (response, duration) = await HttpRequester.RequestPostWithDuration<APIResponse>("/api/complex-operation", jsonData);
        ///     
        ///     if (duration.TotalSeconds > 1.0)
        ///     {
        ///         Debug.LogWarning($"API 응답이 느립니다: {duration.TotalSeconds}초");
        ///     }
        ///     
        ///     return (response, duration);
        /// }
        /// </code>
        /// </example>
        public static async UniTask<(T, TimeSpan)> RequestPostWithDuration<T>(string url, string body, Dictionary<string, string> header = null, bool useAuth = true)
        {
            return await RequestWithDuration<T>(url, body, "post", header, useAuth);
        }

        /// <summary>
        /// GET 방식으로 API를 호출하고 처리 시간도 함께 반환합니다.
        /// </summary>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL (http가 포함되지 않은 경우 Domain과 결합)</param>
        /// <param name="body">요청에 포함할 파라미터 (URL 쿼리스트링으로 변환)</param>
        /// <param name="header">추가할 헤더 정보 (null 가능)</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터와 요청 처리 시간</returns>
        /// <example>
        /// <code>
        /// // 서버 상태 확인 및 응답 시간 측정 예제
        /// public async UniTask<(ServerStatus, TimeSpan)> CheckServerStatus()
        /// {
        ///     var (status, duration) = await HttpRequester.RequestGetWithDuration<ServerStatus>("/api/server/status");
        ///     
        ///     // 응답 시간에 따른 서버 상태 평가
        ///     if (duration.TotalMilliseconds < 100)
        ///     {
        ///         Debug.Log("서버 응답 상태: 매우 좋음");
        ///     }
        ///     else if (duration.TotalMilliseconds < 500)
        ///     {
        ///         Debug.Log("서버 응답 상태: 양호");
        ///     }
        ///     else
        ///     {
        ///         Debug.Log("서버 응답 상태: 지연됨");
        ///     }
        ///     
        ///     return (status, duration);
        /// }
        /// </code>
        /// </example>
        public static async UniTask<(T, TimeSpan)> RequestGetWithDuration<T>(string url, Dictionary<string, object> body = null, Dictionary<string, string> header = null, bool useAuth = true)
        {
            return await RequestWithDuration<T>(url, body == null ? null : JsonHelper.ToJson(body), "get", header, useAuth);
        }

        /// <summary>
        /// HTTP 요청을 처리하고 처리 시간을 측정하는 내부 메소드
        /// </summary>
        /// <remarks>
        /// 이 메소드는 모든 성능 측정이 필요한 HTTP 요청의 공통 로직을 처리합니다:
        /// - 요청 처리 시간 측정
        /// - URL 구성
        /// - HTTP 메소드 설정
        /// - 헤더 설정
        /// - 요청 로깅
        /// - 응답 처리 및 역직렬화
        /// </remarks>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="url">요청할 URL</param>
        /// <param name="body">요청 본문 (JSON 문자열)</param>
        /// <param name="methodString">HTTP 메소드 문자열</param>
        /// <param name="header">추가할 헤더 정보</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <returns>지정된 타입으로 변환된 응답 데이터와 요청 처리 시간의 튜플</returns>
        private static async UniTask<(T, TimeSpan)> RequestWithDuration<T>(string url, string body = null, string methodString = "", Dictionary<string, string> header = null, bool useAuth = true)
        {
            HTTPMethods method = StringToMethod(methodString);
            if (!url.Contains("http")) url = $"{Domain}{url}";
            var request = SelectHTTPRequest(method, url);
            request.DownloadSettings = new Best.HTTP.Request.Settings.DownloadSettings() { ContentStreamMaxBuffered = 1024 * 1024 * 200 };
            request.MethodType = method;
            request.SetHeader("Content-Type", "application/json; charset=utf-8");
            JObject headerObject = new JObject();
            headerObject.Add("Content-Type", "application/json; charset=utf-8");
            if (header != null)
            {
                foreach (var kvp in header)
                {
                    request.SetHeader(kvp.Key, kvp.Value);
                    headerObject.Add(kvp.Key, kvp.Value);
                }
            }
            //ULog.Debug($"RequestWithDuration APIToken :{AuthService.Instance.Entiti.accessToken}");
            //if (useAuth) request.SetHeader("access-token", AuthService.Instance.Entiti.accessToken);
            if (body != null)
            {
                request.UploadSettings.UploadStream =
                        new MemoryStream(Encoding.UTF8.GetBytes(body));
            }

            HttpLogEntry log = ServerLog.LogHttpRequest(url, methodString, headerObject.ToString(Formatting.None), body, DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
            //var response = await request.GetFromJsonResultAsync<T>();
            var now = DateTime.UtcNow;
            var response = await request.GetAsStringAsync();
            var diff = DateTime.UtcNow - now;
            log.ResponseData = response;
            log.ResponseDate = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
            ServerLog.LogHttpResponse(log);
            if (typeof(T) == typeof(string))
            {
                return ((T)(object)response, diff);
            }
            else
            {
                return (JsonHelper.FromJson<T>(response), diff);
            }
        }

        /// <summary>
        /// 원격 서버에서 파일을 다운로드합니다.
        /// </summary>
        /// <param name="url">다운로드할 파일의 URL</param>
        /// <param name="savePath">파일을 저장할 로컬 경로</param>
        /// <param name="OnComplete">다운로드 완료시 호출될 콜백</param>
        /// <param name="OnProgress">다운로드 진행 상태 업데이트 콜백 (progress: 다운로드된 바이트 수, length: 전체 파일 크기)</param>
        /// <param name="OnError">오류 발생시 호출될 콜백</param>
        /// <returns>발행된 HTTP 요청 객체 (파일이 이미 존재하는 경우 null)</returns>
        /// <remarks>
        /// 지정된 경로에 파일이 이미 존재하는 경우 다운로드를 수행하지 않고 바로 OnComplete 콜백을 호출합니다.
        /// </remarks>
        /// <example>
        /// <code>
        /// // 이미지 파일 다운로드 예제
        /// public void DownloadImage(string imageUrl, string fileName)
        /// {
        ///     string savePath = Path.Combine(Application.persistentDataPath, fileName);
        ///     
        ///     HttpRequester.Download(
        ///         imageUrl, 
        ///         savePath,
        ///         () => {
        ///             Debug.Log($"이미지가 성공적으로 다운로드되었습니다: {savePath}");
        ///             // 다운로드된 이미지 사용 코드...
        ///         },
        ///         (progress, length) => {
        ///             float percentage = (float)progress / length * 100f;
        ///             Debug.Log($"다운로드 진행률: {percentage:F1}%");
        ///         },
        ///         (error) => {
        ///             Debug.LogError($"다운로드 실패: {error}");
        ///         }
        ///     );
        /// }
        /// </code>
        /// </example>
        public static HTTPRequest Download(string url, string savePath, Action OnComplete, Action<long, long> OnProgress = null, Action<string> OnError = null)
        {
            ULog.Debug($"Download {url}");
            if (File.Exists(savePath))
            {
                OnComplete.Invoke();
                return null;
            }

            OnRequestFinishedDelegate onRequest = (HTTPRequest req, HTTPResponse resp) =>
            {
                req.DownloadSettings.OnDownloadStarted = null;
                req.DownloadSettings.OnDownloadProgress = null;
                req.DownloadSettings.DownloadStreamFactory = null;

                switch (req.State)
                {
                    case HTTPRequestStates.Finished:
                        if (resp.IsSuccess)
                        {
                            //System.IO.File.WriteAllBytes(savePath, resp.Data);
                            OnComplete.Invoke();
                        }
                        else
                        {
                            ULog.Warning($"Server error({resp.StatusCode} - {resp.Message})! ");
                            OnError?.Invoke($"Server error({resp.StatusCode} - {resp.Message})!");
                        }
                        break;
                    default:
                        ULog.Error(req.State.ToString(), new Exception(resp.Message));
                        OnError?.Invoke(req.State.ToString());
                        break;
                }
            };

            var request = SelectHTTPRequest(HTTPMethods.Get, url, onRequest);

            request.DownloadSettings.OnDownloadStarted += async (HTTPRequest req, HTTPResponse resp, DownloadContentStream stream) =>
            {
                ULog.Debug("Download Started");

                long len = await UniTask.RunOnThreadPool<long>(() => ConsumeDownloadStream(savePath, stream as BlockingDownloadContentStream));
                if (len == 0)
                    OnError?.Invoke($"Download Fail!");

                ULog.Debug($"Download finished!");
            };
            request.DownloadSettings.OnDownloadProgress += (HTTPRequest req, long progress, long length) =>
            {
                ULog.Debug($"Download Progress! progress:{progress} length:{length}");
                OnProgress?.Invoke(progress, length);
            };
            request.DownloadSettings.DownloadStreamFactory = (req, resp, bufferAvailableHandler)
                => new BlockingDownloadContentStream(resp, req.DownloadSettings.ContentStreamMaxBuffered, bufferAvailableHandler);

            return request.Send();
        }

        /// <summary>
        /// 다운로드 스트림을 처리하여 파일로 저장합니다.
        /// </summary>
        /// <remarks>
        /// 이 메소드는 스트림 데이터를 지정된 파일로 저장하며, 다운로드가 완료되거나 중단될 때까지 블로킹 방식으로 데이터를 처리합니다.
        /// 다운로드에 실패한 경우 (길이가 0인 경우) 빈 파일은 삭제됩니다.
        /// </remarks>
        /// <param name="savePath">다운로드한 데이터를 저장할 파일 경로</param>
        /// <param name="blockingStream">다운로드 콘텐츠 스트림</param>
        /// <returns>저장된 파일의 크기(바이트)</returns>
        /// <example>
        /// <code>
        /// // 내부적으로 Download 메소드에서 사용됩니다
        /// long fileSize = await UniTask.RunOnThreadPool<long>(() => 
        ///     ConsumeDownloadStream(savePath, downloadStream as BlockingDownloadContentStream));
        /// </code>
        /// </example>
        private static long ConsumeDownloadStream(string savePath, BlockingDownloadContentStream blockingStream)
        {
            long len = 0;
            using (FileStream fs = new FileStream(savePath, System.IO.FileMode.Create))
            {
                try
                {
                    //ULog.Debug($"ConsumeDownloadStream blockingStream:{blockingStream == null}");
                    while (blockingStream != null && !blockingStream.IsCompleted)
                    {
                        if (blockingStream.TryTake(out var buffer))
                        {
                            try
                            {
                                //ULog.Debug($"ConsumeDownloadStream buffer.Data:{buffer.Data.Length}, Offset:{buffer.Offset}, Count:{buffer.Count}");
                                fs.Write(buffer.Data, buffer.Offset, buffer.Count);
                            }
                            finally
                            {
                                BufferPool.Release(buffer);
                            }
                        }
                    }
                }
                finally
                {
                    if (blockingStream != null) blockingStream.Dispose();

                    len = fs.Length;
                }
            }
            if (len == 0) File.Delete(savePath);
            return len;

        }

        /// <summary>
        /// HTTP 메소드와 URL을 기반으로 적절한 HTTPRequest 객체를 생성합니다.
        /// </summary>
        /// <param name="methods">요청에 사용할 HTTP 메소드</param>
        /// <param name="url">요청할 URL</param>
        /// <param name="onRequest">요청 완료 시 호출될 콜백 (선택 사항)</param>
        /// <returns>생성된 HTTPRequest 객체</returns>
        /// <example>
        /// <code>
        /// // 내부 사용 예제 (직접 사용하지 않음)
        /// HTTPRequest request = SelectHTTPRequest(HTTPMethods.Get, "https://api.example.com/data");
        /// request.SetHeader("Custom-Header", "HeaderValue");
        /// request.Send();
        /// </code>
        /// </example>
        private static HTTPRequest SelectHTTPRequest(HTTPMethods methods, string url, OnRequestFinishedDelegate onRequest = null)
        {
            switch (methods)
            {
                case HTTPMethods.Get:

                    if (onRequest != null)
                        return HTTPRequest.CreateGet(url, onRequest);
                    else
                        return HTTPRequest.CreateGet(url);
                case HTTPMethods.Post:
                    return HTTPRequest.CreatePost(url);
            }

            return null;
        }

        /// <summary>
        /// 문자열 형태의 HTTP 메소드를 Best.HTTP.HTTPMethods 열거형으로 변환합니다.
        /// </summary>
        /// <param name="method">변환할 HTTP 메소드 문자열 ("get", "post" 등)</param>
        /// <returns>해당하는 HTTPMethods 열거형 값</returns>
        /// <exception cref="ArgumentException">지원하지 않는 HTTP 메소드인 경우 발생</exception>
        private static HTTPMethods StringToMethod(string method)
        {
            return method.ToLower() switch
            {
                "get" => HTTPMethods.Get,
                "post" => HTTPMethods.Post,
                _ => throw new ArgumentException($"Unsupported HTTP method: {method}"),
            };
        }
    }

}
