using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Threading;
using UVC.Network;

namespace AssetsUVC.Network
{
    /// <summary>
    /// HTTP 요청을 디바운스(debounce) 처리하기 위한 유틸리티 클래스
    /// </summary>
    /// <remarks>
    /// 이 클래스는 짧은 시간 내에 반복적으로 발생하는 HTTP 요청을 제어하여
    /// 마지막 요청만 실행되도록 합니다. 사용자 입력에 따른 API 호출이나
    /// 실시간 검색과 같이 연속된 요청을 최적화할 때 유용합니다.
    /// 
    /// 기본 사용법:
    /// <code>
    /// // DebounceRequester 인스턴스 생성
    /// var requester = new DebounceRequester();
    /// 
    /// // 연속된 검색 요청 예제
    /// void OnSearchTextChanged(string searchText) 
    /// {
    ///     requester.Request<SearchResult>(
    ///         300, // 300ms 디바운스
    ///         "/api/search",
    ///         "get",
    ///         $"{{\"query\": \"{searchText}\"}}",
    ///         null,
    ///         false,
    ///         result => {
    ///             // 검색 결과 처리
    ///             DisplaySearchResults(result);
    ///         }
    ///     );
    /// }
    /// </code>
    /// </remarks>
    public class DebounceRequester
    {
        /// <summary>
        /// 취소 토큰 소스. 이전 요청을 취소하는 데 사용됩니다.
        /// </summary>
        private CancellationTokenSource cts = new CancellationTokenSource();

        /// <summary>
        /// 취소 토큰을 재설정하여 이전 요청을 취소합니다.
        /// </summary>
        /// <remarks>
        /// 이 메소드는 새로운 요청이 시작될 때마다 호출되어 이전에 예약된 요청을 취소합니다.
        /// </remarks>
        private void resetCancellationToken()
        {
            cts.Cancel();
            cts = new CancellationTokenSource();
        }

        /// <summary>
        /// DebounceRequester가 사용한 리소스를 해제합니다.
        /// </summary>
        /// <remarks>
        /// 이 클래스의 사용이 끝나면 항상 Dispose를 호출하여 리소스 누수를 방지해야 합니다.
        /// </remarks>
        /// <example>
        /// <code>
        /// // 리소스 해제 예제
        /// public class SearchManager : IDisposable
        /// {
        ///     private DebounceRequester requester = new DebounceRequester();
        ///     
        ///     public void Dispose()
        ///     {
        ///         requester.Dispose();
        ///     }
        /// }
        /// </code>
        /// </example>
        public void Dispose()
        {
            cts.Cancel();
            cts.Dispose();
        }


        /// <summary>
        /// 지정된 시간(밀리초) 동안 디바운스된 HTTP 요청을 수행합니다.
        /// </summary>
        /// <typeparam name="T">응답 데이터를 변환할 타입</typeparam>
        /// <param name="milliseconds">디바운스 시간(밀리초)</param>
        /// <param name="url">요청할 URL (http가 포함되지 않은 경우 Domain과 결합)</param>
        /// <param name="method">HTTP 메소드 (get, post 등)</param>
        /// <param name="body">요청 본문으로 전송할 JSON 문자열 (null 가능)</param>
        /// <param name="header">추가할 헤더 정보 (null 가능)</param>
        /// <param name="useAuth">인증 토큰 사용 여부</param>
        /// <param name="action">요청 완료 후 실행할 콜백 함수 (null 가능)</param>
        /// <remarks>
        /// 이 메소드는 호출된 후 지정된 밀리초 동안 대기하고, 그 시간 내에 다시 호출되면
        /// 이전 요청을 취소하고 새로운 타이머를 시작합니다. 디바운스 시간이 경과한 후에만
        /// 실제 HTTP 요청이 수행됩니다.
        /// </remarks>
        /// <example>
        /// <code>
        /// // 실시간 타이핑 검색 예제
        /// private DebounceRequester searchRequester = new DebounceRequester();
        /// 
        /// public void OnSearchInputChanged(string searchText)
        /// {
        ///     // 사용자가 타이핑을 멈춘 후 500ms 후에 검색 요청
        ///     searchRequester.Request<List<ProductData>>(
        ///         500,
        ///         "/api/products/search",
        ///         "get",
        ///         $"{{\"query\": \"{searchText}\"}}",
        ///         null,
        ///         true,
        ///         results => {
        ///             // 검색 결과 UI 업데이트
        ///             UpdateSearchResultsUI(results);
        ///         }
        ///     );
        /// }
        /// 
        /// // 자동 저장 기능 예제
        /// private DebounceRequester autoSaveRequester = new DebounceRequester();
        /// 
        /// public void OnDocumentChanged(string documentId, string content)
        /// {
        ///     // 문서 변경 후 2초 동안 추가 변경이 없으면 저장
        ///     var body = $"{{\"id\": \"{documentId}\", \"content\": \"{content}\"}}";
        ///     
        ///     autoSaveRequester.Request<SaveResult>(
        ///         2000,
        ///         "/api/documents/save",
        ///         "post",
        ///         body,
        ///         null,
        ///         true,
        ///         result => {
        ///             if (result.success) {
        ///                 ShowSavedNotification();
        ///             }
        ///         }
        ///     );
        /// }
        /// </code>
        /// </example>
        public void Request<T>(int milliseconds, string url, string method, string body = null, Dictionary<string, string> header = null, bool useAuth = false, Action<T> action = null)
        {
            resetCancellationToken();
            UniTask.Delay(TimeSpan.FromMilliseconds(milliseconds), cancellationToken: cts.Token, ignoreTimeScale: false).ContinueWith(async () =>
            {
                if (action != null)
                {
                    T result = await HttpRequester.Request<T>(url, method, body, header, useAuth);
                    action.Invoke(result);
                }
                else
                {
                    HttpRequester.Request<T>(url, method, body, header, useAuth).Forget();
                }
            });
        }

    }
}
