#nullable enable

using Cysharp.Threading.Tasks;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using UnityEngine;
using UVC.Data;

namespace UVC.Tests.Data
{
    /// <summary>
    /// HttpPipeLine 클래스의 테스트를 위한 테스트 클래스입니다.
    /// </summary>
    [TestFixture]
    public class HttpPipeLineTests
    {
        // 테스트에 사용할 HttpPipeLine 인스턴스
        private HttpPipeLine pipeLine;

        /// <summary>
        /// 각 테스트 실행 전에 호출되는 설정 메서드입니다.
        /// </summary>
        [SetUp]
        public void Setup()
        {
            pipeLine = new HttpPipeLine();
            pipeLine.UseMockup = true; // MockHttpRequester 사용 설정
            // 테스트를 위한 DataRepository 초기화
            ClearDataRepository();
        }

        /// <summary>
        /// 모든 테스트 메서드를 실행하는 메서드입니다.
        /// </summary>
        /// <remarks>
        /// 이 메서드는 클래스의 모든 테스트 메서드를 순차적으로 호출하고 
        /// 각 테스트의 성공 또는 실패 여부를 로그로 출력합니다.
        /// </remarks>
        public async UniTask TestAll()
        {
            Setup();

            Debug.Log("===== HttpPipeLine 테스트 시작 =====");
            //RunTest(nameof(Add_NewInfo_AddedSuccessfully), Add_NewInfo_AddedSuccessfully);
            //RunTest(nameof(Add_ExistingInfo_UpdatesExistingEntry), Add_ExistingInfo_UpdatesExistingEntry);
            //await RunTestAsync(nameof(Remove_ExistingInfo_RemovedSuccessfullyAsync), Remove_ExistingInfo_RemovedSuccessfullyAsync);
            //await RunTestAsync(nameof(Remove_NonExistingInfo_DoesNothing), Remove_NonExistingInfo_DoesNothing);
            //RunTest(nameof(Excute_WithNonExistingKey_DoesNothing), Excute_WithNonExistingKey_DoesNothing);
            //await RunTestAsync(nameof(Excute_WithJObjectResponse_ProcessesDataCorrectly), Excute_WithJObjectResponse_ProcessesDataCorrectly);
            //await RunTestAsync(nameof(Excute_WithJArrayResponse_ProcessesDataCorrectly), Excute_WithJArrayResponse_ProcessesDataCorrectly);
            //await RunTestAsync(nameof(Test_Excute_AgvDataParsing), Test_Excute_AgvDataParsing);
            //await RunTestAsync(nameof(Test_Excute_AlarmDataParsing), Test_Excute_AlarmDataParsing);
            //await RunTestAsync(nameof(Test_Excute_MultipleDataTypes), Test_Excute_MultipleDataTypes);
            //await RunTestAsync(nameof(Test_Excute_CarrierDataParsing), Test_Excute_CarrierDataParsing);
            //await RunTestAsync(nameof(Test_Excute_BaseInfoDataParsing), Test_Excute_BaseInfoDataParsing);
            //await RunTestAsync(nameof(Test_Excute_WithRepeatExecution), Test_Excute_WithRepeatExecution);
            //await RunTestAsync(nameof(Test_StopRepeat_StopsExecutionCorrectly), Test_StopRepeat_StopsExecutionCorrectly);
            //await RunTestAsync(nameof(Test_MultipleRepeatingRequests_ManagedIndependently), Test_MultipleRepeatingRequests_ManagedIndependently);
            //await RunTestAsync(nameof(Test_RepeatWithCount_StopsAutomatically), Test_RepeatWithCount_StopsAutomatically);

            // HttpResponseMask 테스트 추가
            Debug.Log("===== HttpResponseMask 테스트 시작 =====");
            RunTest(nameof(HttpResponseMask_Apply_SuccessfulResponse_ReturnsSuccessWithData), HttpResponseMask_Apply_SuccessfulResponse_ReturnsSuccessWithData);
            RunTest(nameof(HttpResponseMask_Apply_FailedResponse_WrongSuccessValue_ReturnsFailWithMessage), HttpResponseMask_Apply_FailedResponse_WrongSuccessValue_ReturnsFailWithMessage);
            RunTest(nameof(HttpResponseMask_Apply_FailedResponse_MissingSuccessKey_ReturnsFailWithMessage), HttpResponseMask_Apply_FailedResponse_MissingSuccessKey_ReturnsFailWithMessage);
            RunTest(nameof(HttpResponseMask_Apply_FailedResponse_MissingDataKey_ReturnsFailWithMessage), HttpResponseMask_Apply_FailedResponse_MissingDataKey_ReturnsFailWithMessage);
            RunTest(nameof(HttpResponseMask_Apply_SuccessfulResponse_CustomKeys_ReturnsSuccessWithData), HttpResponseMask_Apply_SuccessfulResponse_CustomKeys_ReturnsSuccessWithData);
            RunTest(nameof(HttpResponseMask_Apply_InvalidJson_ThrowsException), HttpResponseMask_Apply_InvalidJson_ThrowsException);
            Debug.Log("===== HttpResponseMask 테스트 완료 =====");

            Debug.Log("===== HttpPipeLine 테스트 완료 =====");
        }

        /// <summary>
        /// 단일 테스트 메서드를 실행하고 결과를 로그로 출력합니다.
        /// </summary>
        /// <param name="testName">테스트 메서드 이름</param>
        /// <param name="testAction">실행할 테스트 메서드</param>
        private void RunTest(string testName, Action testAction)
        {
            try
            {
                Debug.Log($"테스트 시작: {testName}");
                testAction();
                Debug.Log($"테스트 성공: {testName}");
            }
            catch (Exception ex)
            {
                Debug.LogError($"테스트 실패: {testName}\n{ex.Message}\n{ex.StackTrace}");
            }
        }

        private async UniTask RunTestAsync(string testName, Func<UniTask> testAction)
        {
            try
            {
                Debug.Log($"테스트 시작: {testName}");
                await testAction();
                Debug.Log($"테스트 성공: {testName}");
            }
            catch (Exception ex)
            {
                Debug.LogError($"테스트 실패: {testName}\n{ex.Message}\n{ex.StackTrace}");
            }
        }

        /// <summary>
        /// 새로운 HttpPipeLineInfo를 추가하는 테스트
        /// </summary>
        [Test]
        public void Add_NewInfo_AddedSuccessfully()
        {
            // Arrange
            var info = new HttpPipeLineInfo("http://test.com");

            // Act
            pipeLine.Add("test", info);

            // Assert - Dictionary에 추가되었는지 확인 (reflection 사용)
            var infoList = GetInfoListField();
            Assert.IsTrue(infoList.ContainsKey("test"));
            Assert.AreEqual(info, infoList["test"]);
        }

        /// <summary>
        /// 기존에 존재하는 키로 HttpPipeLineInfo를 추가할 때 업데이트 테스트
        /// </summary>
        [Test]
        public void Add_ExistingInfo_UpdatesExistingEntry()
        {
            // Arrange
            var info1 = new HttpPipeLineInfo("http://test1.com");
            var info2 = new HttpPipeLineInfo("http://test2.com");
            pipeLine.Add("test", info1);

            // Act
            pipeLine.Add("test", info2);

            // Assert
            var infoList = GetInfoListField();
            Assert.IsTrue(infoList.ContainsKey("test"));
            Assert.AreEqual(info2, infoList["test"]);
            Assert.AreNotEqual(info1, infoList["test"]);
        }

        /// <summary>
        /// 존재하는 HttpPipeLineInfo를 제거하는 테스트
        /// </summary>
        [Test]
        public async UniTask Remove_ExistingInfo_RemovedSuccessfullyAsync()
        {
            // Arrange
            var info = new HttpPipeLineInfo("http://test.com");
            pipeLine.Add("test", info);

            // Act
            await pipeLine.RemoveAsync("test");

            // Assert
            var infoList = GetInfoListField();
            Assert.IsFalse(infoList.ContainsKey("test"));
        }

        /// <summary>
        /// 존재하지 않는 키에 대한 Remove 호출 테스트
        /// </summary>
        [Test]
        public async UniTask Remove_NonExistingInfo_DoesNothing()
        {
            // Arrange
            var info = new HttpPipeLineInfo("http://test.com");
            pipeLine.Add("test", info);

            // Act - 존재하지 않는 키 제거 시도
            await pipeLine.RemoveAsync("nonexistent");

            // Assert - 기존 항목은 여전히 존재해야 함
            var infoList = GetInfoListField();
            Assert.IsTrue(infoList.ContainsKey("test"));
            Assert.AreEqual(info, infoList["test"]);
        }

        /// <summary>
        /// HttpPipeLine의 private infoList 필드 가져오기
        /// </summary>
        private Dictionary<string, HttpPipeLineInfo> GetInfoListField()
        {
            var fieldInfo = typeof(HttpPipeLine).GetField("infoList",
                BindingFlags.NonPublic | BindingFlags.Instance);

            return (Dictionary<string, HttpPipeLineInfo>)fieldInfo.GetValue(pipeLine);
        }

        /// <summary>
        /// DataRepository를 테스트를 위해 초기화 (리플렉션 사용)
        /// </summary>
        private void ClearDataRepository()
        {
            var repositoryType = typeof(DataRepository);
            var instanceField = repositoryType.GetField("instance",
                BindingFlags.NonPublic | BindingFlags.Static);

            if (instanceField != null)
            {
                // Lazy<DataRepository> 필드 값을 가져옴
                var lazyInstance = instanceField.GetValue(null);

                // Lazy<T>의 Value 속성을 통해 인스턴스 접근
                var instance = lazyInstance.GetType().GetProperty("Value").GetValue(lazyInstance);

                // dataObjects 딕셔너리 필드 가져오기
                var dataObjectsField = repositoryType.GetField("dataObjects",
                    BindingFlags.NonPublic | BindingFlags.Instance);

                // 딕셔너리 접근하여 Clear 메서드 호출
                var dataObjects = dataObjectsField.GetValue(instance) as Dictionary<string, IDataObject>;
                dataObjects.Clear();
            }
        }

        /// <summary>
        /// Excute 메소드에서 JObject 응답을 처리하는 기능 테스트
        /// </summary>
        [Test]
        public async UniTask Excute_WithJObjectResponse_ProcessesDataCorrectly()
        {
            // Arrange
            bool handlerCalled = false;
            DataObject? receivedData = null;

            var mockResponse = @"{""message"": ""Success"", ""data"": {""name"": ""테스트"", ""value"": 123}}";

            // DataMask와 DataMapper 설정
            var dataMask = new DataMask();
            dataMask["name"] = "이름";
            dataMask["value"] = 0;

            var dataMapper = new DataMapper(dataMask);

            // HttpPipeLineInfo 설정
            var info = new HttpPipeLineInfo("http://test.com")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) =>
                {
                    handlerCalled = true;
                    if (data is DataObject dataObject)
                    {
                        receivedData = dataObject;
                    }
                });

            pipeLine.Add("testKey", info);

            try
            {
                // MockHttpRequester에 테스트용 응답 설정
                MockHttpRequester.SetResponse("http://test.com", mockResponse);

                // Act
                await pipeLine.Excute("testKey");

                // Assert
                Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
                Assert.IsNotNull(receivedData, "핸들러에 전달된 데이터가 null입니다.");
                Assert.AreEqual("테스트", receivedData?.GetString("name"));
                Assert.AreEqual(123, receivedData?.GetInt("value"));
            }
            finally
            {
                // 설정한 응답 정리
                MockHttpRequester.ClearResponses("http://test.com");
                await pipeLine.RemoveAsync("testKey");
            }
        }

        /// <summary>
        /// Excute 메소드에서 JArray 응답을 처리하는 기능 테스트
        /// </summary>
        [Test]
        public async UniTask Excute_WithJArrayResponse_ProcessesDataCorrectly()
        {
            // Arrange
            bool handlerCalled = false;
            IDataObject? receivedData = null;

            var mockResponse = @"{""message"": ""Success"", ""data"": [{""name"": ""항목1"", ""value"": 10}, {""name"": ""항목2"", ""value"": 20}]}";

            // 배열용 DataMask 설정
            var dataMask = new DataMask
            {
                ["name"] = "이름",
                ["value"] = 0
            };

            var dataMapper = new DataMapper(dataMask);

            // HttpPipeLineInfo 설정
            var info = new HttpPipeLineInfo("http://test.com")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) =>
                {
                    handlerCalled = true;
                    receivedData = data;
                });

            pipeLine.Add("testArrayKey", info);

            try
            {
                // MockHttpRequester에 테스트용 응답 설정
                MockHttpRequester.SetResponse("http://test.com", mockResponse);

                // Act
                await pipeLine.Excute("testArrayKey");


                // Assert
                Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
                Assert.IsNotNull(receivedData, "핸들러에 전달된 데이터가 null입니다.");
                Assert.IsTrue(receivedData is DataArray, "결과가 DataArray가 아닙니다.");

                var dataArray = receivedData as DataArray;
                Assert.AreEqual(2, dataArray?.Count);
                Assert.AreEqual("항목1", dataArray?[0].GetString("name"));
                Assert.AreEqual(10, dataArray?[0].GetInt("value"));
                Assert.AreEqual("항목2", dataArray?[1].GetString("name"));
                Assert.AreEqual(20, dataArray?[1].GetInt("value"));
            }
            finally
            {
                // 설정한 응답 정리
                MockHttpRequester.ClearResponses("http://test.com");
                await pipeLine.RemoveAsync("testArrayKey");
            }
        }

        /// <summary>
        /// Excute 메소드에서 존재하지 않는 키를 호출했을 때의 동작 테스트
        /// </summary>
        [Test]
        public void Excute_WithNonExistingKey_DoesNothing()
        {
            // Arrange - 의도적으로 아무 것도 설정하지 않음

            // Act & Assert - 예외가 발생하지 않아야 함
            Assert.DoesNotThrow(() => pipeLine.Excute("nonExistingKey"));
        }

        /// <summary>
        /// UVC.Tests.MockHttpRequester를 사용하여 AGV 데이터 파싱 테스트
        /// </summary>
        [Test]
        public async UniTask Test_Excute_AgvDataParsing()
        {
            // Arrange
            bool handlerCalled = false;
            IDataObject? receivedData = null;
            string agvUrl = "http://api.example.com/agv";  // MockHttpRequester는 url에 "agv"가 포함된 경우 AGV 관련 응답 반환

            // 배열용 DataMask 설정 (AGV 데이터 구조에 맞춤)
            var dataMask = new DataMask
            {
                ["VHL_NAME"] = "차량명",
                ["AGV_IDX"] = "인덱스",
                ["B_INSTALL"] = "설치여부",
                ["NODE_ID"] = "노드ID",
                ["REAL_ID"] = "실제ID",
                ["VHL_STATE"] = "상태",
                ["BAY_LIST"] = "베이리스트"
            };

            var dataMapper = new DataMapper(dataMask);

            // HttpPipeLineInfo 설정
            var info = new HttpPipeLineInfo(agvUrl, "get")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) =>
                {
                    handlerCalled = true;
                    receivedData = data;
                });

            pipeLine.Add("agvData", info);

            // Act
            await pipeLine.Excute("agvData");

            // Assert
            Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
            Assert.IsNotNull(receivedData, "데이터가 null입니다.");
            Assert.IsTrue(receivedData is DataArray, "결과가 DataArray가 아닙니다.");

            var dataArray = receivedData as DataArray;
            Assert.IsTrue(dataArray?.Count > 0, "데이터 배열이 비어 있습니다.");

            // 첫 번째 항목 확인
            Assert.IsNotNull(dataArray?[0].GetString("VHL_NAME"));
            Assert.IsNotNull(dataArray?[0].GetString("AGV_IDX"));
            Assert.IsNotNull(dataArray?[0].GetString("B_INSTALL"));
        }

        /// <summary>
        /// UVC.Tests.MockHttpRequester를 사용하여 알람 데이터 파싱 테스트
        /// </summary>
        [Test]
        public async UniTask Test_Excute_AlarmDataParsing()
        {
            // Arrange
            bool handlerCalled = false;
            IDataObject? receivedData = null;
            string alarmUrl = "http://api.example.com/alarm"; // MockHttpRequester는 url에 "alarm"이 포함된 경우 알람 관련 응답 반환

            // 배열용 DataMask 설정 (알람 데이터 구조에 맞춤)
            var dataMask = new DataMask
            {
                ["ID"] = "알람ID",
                ["ALARM_TYPE"] = "알람타입",
                ["LEVEL"] = "심각도",
                ["STATE"] = "상태",
                ["MESSAGE"] = "메시지",
                ["CODE"] = "코드",
                ["ICON"] = "아이콘",
                ["SET_TIME"] = "발생시간"
            };

            var dataMapper = new DataMapper(dataMask);

            // HttpPipeLineInfo 설정
            var info = new HttpPipeLineInfo(alarmUrl, "get")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) =>
                {
                    handlerCalled = true;
                    receivedData = data;
                });

            pipeLine.Add("alarmData", info);

            try
            {
                // Act
                await pipeLine.Excute("alarmData");

                // Assert
                Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
                Assert.IsNotNull(receivedData, "데이터가 null입니다.");
                Assert.IsTrue(receivedData is DataArray, "결과가 DataArray가 아닙니다.");

                var dataArray = receivedData as DataArray;
                Assert.IsTrue(dataArray?.Count > 0, "데이터 배열이 비어 있습니다.");

                // 첫 번째 항목 확인
                Assert.IsNotNull(dataArray?[0].GetString("ID"));
                Assert.IsNotNull(dataArray?[0].GetString("ALARM_TYPE"));
                Assert.IsNotNull(dataArray?[0].GetString("LEVEL"));
                Assert.IsNotNull(dataArray?[0].GetString("MESSAGE"));
                Assert.IsNotNull(dataArray?[0].GetString("CODE"));
                Assert.IsNotNull(dataArray?[0].GetString("ICON"));
                Assert.IsNotNull(dataArray?[0].GetString("SET_TIME"));
            }
            finally
            {
                await pipeLine.RemoveAsync("alarmData");
            }
        }

        /// <summary>
        /// UVC.Tests.MockHttpRequester를 사용하여 여러 URL 유형에 대한 동시 테스트
        /// </summary>
        [Test]
        public async UniTask Test_Excute_MultipleDataTypes()
        {
            // Arrange
            int handlerCallCount = 0;
            Dictionary<string, IDataObject?> results = new Dictionary<string, IDataObject?>();

            // 각 데이터 타입별 URL
            Dictionary<string, string> urls = new Dictionary<string, string>
            {
                { "agv", "http://api.example.com/agv" },
                { "equipment", "http://api.example.com/equipment" },
                { "alarm", "http://api.example.com/alarm" }
            };

            // 각 데이터 타입에 대한 DataMask 설정
            Dictionary<string, DataMask> dataMasks = new Dictionary<string, DataMask>
            {
                {
                    "agv", new DataMask
                    {
                        ["0"] = new DataMask { ["VHL_NAME"] = "차량명", ["AGV_IDX"] = "인덱스" }
                    }
                },
                {
                    "equipment", new DataMask
                    {
                        ["0"] = new DataMask { ["EQP_ID"] = "장비ID", ["KOR_EQP_NAME"] = "장비명" }
                    }
                },
                {
                    "alarm", new DataMask
                    {
                        ["0"] = new DataMask { ["ID"] = "알람ID", ["ALARM_TYPE"] = "알람타입" }
                    }
                }
            };

            // 각 데이터 타입별 HttpPipeLineInfo 설정 및 등록
            foreach (var item in urls)
            {
                string key = item.Key;
                var info = new HttpPipeLineInfo(item.Value, "get")
                    .setDataMapper(new DataMapper(dataMasks[key]))
                    .setSuccessHandler((data) =>
                    {
                        handlerCallCount++;
                        results[key] = data;
                    });

                pipeLine.Add(key, info);
                await pipeLine.Excute(key);
            }


            // Assert
            Assert.AreEqual(urls.Count, handlerCallCount, "모든 핸들러가 호출되지 않았습니다.");
            Assert.AreEqual(urls.Count, results.Count, "모든 결과가 수집되지 않았습니다.");

            // 각 데이터 타입별 결과 확인
            foreach (var result in results)
            {
                Assert.IsNotNull(result.Value, $"{result.Key} 데이터가 null입니다.");
                Assert.IsTrue(result.Value is DataArray, $"{result.Key} 결과가 DataArray가 아닙니다.");

                var dataArray = result.Value as DataArray;
                Assert.IsTrue(dataArray?.Count > 0, $"{result.Key} 데이터 배열이 비어 있습니다.");
            }
        }

        /// <summary>
        /// UVC.Tests.MockHttpRequester를 사용한 운반대(Carrier) 데이터 테스트
        /// </summary>
        [Test]
        public async UniTask Test_Excute_CarrierDataParsing()
        {
            // Arrange
            bool handlerCalled = false;
            IDataObject? receivedData = null;
            string testUrl = "http://api.example.com/carrier"; // url에 "carrier"가 포함된 경우 캐리어 관련 응답 반환

            // DataMask와 DataMapper 설정
            var dataMask = new DataMask
            {
                ["MAIN_CARR_ID"] = "캐리어ID",
                ["SUB_CARR_ID"] = "서브ID",
                ["CARR_SEQ"] = "순번",
                ["CARR_USE"] = "사용상태"
            };

            var dataMapper = new DataMapper(dataMask);

            // HttpPipeLineInfo 설정
            var info = new HttpPipeLineInfo(testUrl, "get")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) =>
                {
                    handlerCalled = true;
                    receivedData = data;
                });

            pipeLine.Add("testCarrierData", info);

            // Act
            await pipeLine.Excute("testCarrierData");

            // Assert
            Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
            Assert.IsNotNull(receivedData, "응답 데이터가 null입니다.");
            Assert.IsTrue(receivedData is DataArray, "응답이 DataArray가 아닙니다.");

            var dataArray = receivedData as DataArray;
            Assert.IsTrue(dataArray?.Count > 0, "데이터 배열이 비어있습니다.");

            // 첫 번째 캐리어 데이터 확인
            Assert.IsNotNull(dataArray?[0].GetString("MAIN_CARR_ID"), "캐리어ID가 null입니다.");
            Assert.IsNotNull(dataArray?[0].GetString("SUB_CARR_ID"), "서브ID가 null입니다.");
            Assert.IsNotNull(dataArray?[0].GetString("CARR_SEQ"), "순번이 null입니다.");
            Assert.IsNotNull(dataArray?[0].GetString("CARR_USE"), "사용상태가 null입니다.");
        }

        /// <summary>
        /// UVC.Tests.MockHttpRequester를 사용하여 BaseInfo 데이터 파싱 테스트
        /// </summary>
        [Test]
        public async UniTask Test_Excute_BaseInfoDataParsing()
        {
            // Arrange
            bool handlerCalled = false;
            IDataObject? receivedData = null;
            string baseInfoUrl = "http://api.example.com/baseinfo"; // url에 "baseinfo"가 포함된 경우 기본 정보 응답 반환

            // BaseInfo에서 테스트할 주요 섹션 (AGV, EQUIPMENT, ALARM 등)에 대한 DataMask 설정
            var dataMask = new DataMask
            {
                ["AGV"] = new DataMask
                {
                    ["0"] = new DataMask
                    {
                        ["VHL_NAME"] = "차량명",
                        ["AGV_IDX"] = "인덱스",
                        ["B_INSTALL"] = "설치여부",
                        ["NODE_ID"] = "노드ID",
                        ["VHL_STATE"] = "상태"
                    }
                },
                ["EQUIPMENT"] = new DataMask
                {
                    ["0"] = new DataMask
                    {
                        ["EQP_ID"] = "장비ID",
                        ["KOR_EQP_NAME"] = "장비명",
                        ["STATE_ID"] = "상태",
                        ["NTW_STS"] = "네트워크상태"
                    }
                },
                ["ALARM"] = new DataMask
                {
                    ["0"] = new DataMask
                    {
                        ["ID"] = "알람ID",
                        ["ALARM_TYPE"] = "알람타입",
                        ["LEVEL"] = "심각도",
                        ["MESSAGE"] = "메시지"
                    }
                }
            };

            var dataMapper = new DataMapper(dataMask);

            // HttpPipeLineInfo 설정
            var info = new HttpPipeLineInfo(baseInfoUrl, "get")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) =>
                {
                    handlerCalled = true;
                    receivedData = data;
                });

            pipeLine.Add("baseInfoData", info);

            try
            {
                // Act
                await pipeLine.Excute("baseInfoData");

                // Assert
                Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
                Assert.IsNotNull(receivedData, "데이터가 null입니다.");
                Assert.IsTrue(receivedData is DataObject, "결과가 DataObject가 아닙니다.");

                var dataObject = receivedData as DataObject;

                // AGV 데이터 검증
                Assert.IsNotNull(dataObject?.Get("AGV"), "AGV 데이터가 없습니다.");
                var agvData = dataObject?.Get("AGV") as DataArray;
                Assert.IsTrue(agvData?.Count > 0, "AGV 데이터 배열이 비어 있습니다.");
                Assert.IsNotNull(agvData?[0].GetString("VHL_NAME"), "VHL_NAME 필드가 없습니다.");
                Assert.IsNotNull(agvData?[0].GetString("AGV_IDX"), "AGV_IDX 필드가 없습니다.");
                Assert.IsNotNull(agvData?[0].GetString("B_INSTALL"), "B_INSTALL 필드가 없습니다.");

                // EQUIPMENT 데이터 검증
                Assert.IsNotNull(dataObject?.Get("EQUIPMENT"), "EQUIPMENT 데이터가 없습니다.");
                var equipmentData = dataObject?.Get("EQUIPMENT") as DataArray;
                Assert.IsTrue(equipmentData?.Count > 0, "EQUIPMENT 데이터 배열이 비어 있습니다.");
                Assert.IsNotNull(equipmentData?[0].GetString("EQP_ID"), "EQP_ID 필드가 없습니다.");
                Assert.IsNotNull(equipmentData?[0].GetString("KOR_EQP_NAME"), "KOR_EQP_NAME 필드가 없습니다.");
                Assert.IsNotNull(equipmentData?[0].GetString("STATE_ID"), "STATE_ID 필드가 없습니다.");

                // ALARM 데이터 검증
                Assert.IsNotNull(dataObject?.Get("ALARM"), "ALARM 데이터가 없습니다.");
                var alarmData = dataObject?.Get("ALARM") as DataArray;
                Assert.IsTrue(alarmData?.Count > 0, "ALARM 데이터 배열이 비어 있습니다.");
                Assert.IsNotNull(alarmData?[0].GetString("ID"), "ID 필드가 없습니다.");
                Assert.IsNotNull(alarmData?[0].GetString("ALARM_TYPE"), "ALARM_TYPE 필드가 없습니다.");
                Assert.IsNotNull(alarmData?[0].GetString("LEVEL"), "LEVEL 필드가 없습니다.");
                Assert.IsNotNull(alarmData?[0].GetString("MESSAGE"), "MESSAGE 필드가 없습니다.");
            }
            finally
            {
                await pipeLine.RemoveAsync("baseInfoData");
            }
        }

        /// <summary>
        /// 반복 실행 기능을 테스트합니다.
        /// </summary>
        [Test]
        public async UniTask Test_Excute_WithRepeatExecution()
        {
            // Arrange
            int handlerCallCount = 0;
            List<string> receivedResponses = new List<string>();
            string testUrl = "http://test.com/repeat";
            int expectedCallCount = 3;
            int repeatInterval = 100; // 테스트를 빠르게 진행하기 위해 간격을 짧게 설정

            // 여러 응답을 순차적으로 반환하기 위한 응답 데이터 설정
            string[] mockResponses = new string[]
            {
        @"{""message"": ""Success"", ""data"": {""id"": 1, ""status"": ""pending"", ""timestamp"": ""2025-06-09T10:00:00Z""}}",
        @"{""message"": ""Success"", ""data"": {""id"": 1, ""status"": ""processing"", ""timestamp"": ""2025-06-09T10:00:10Z""}}",
        @"{""message"": ""Success"", ""data"": {""id"": 1, ""status"": ""completed"", ""timestamp"": ""2025-06-09T10:00:20Z""}}"
            };

            // Mock 응답 설정
            MockHttpRequester.SetResponse(testUrl, mockResponses[0]);

            // DataMask와 DataMapper 설정
            var dataMask = new DataMask();
            dataMask["id"] = 0;
            dataMask["status"] = "";
            dataMask["timestamp"] = "";

            var dataMapper = new DataMapper(dataMask);

            // 반복 실행 설정을 포함한 HttpPipeLineInfo 생성
            var info = new HttpPipeLineInfo(testUrl, "get")
                .setDataMapper(dataMapper)
                .setSuccessHandler(async (data) =>
                {
                    handlerCallCount++;
                    if (data is DataObject dataObject)
                    {
                        receivedResponses.Add(dataObject.GetString("status"));
                    }

                    // 반복 실행 중단
                    if (handlerCallCount >= expectedCallCount)
                    {
                        await pipeLine.StopRepeat("repeatTest");
                    }
                    else
                    {
                        MockHttpRequester.SetResponse(testUrl, mockResponses[handlerCallCount]);
                    }
                })
                .setRepeat(true, expectedCallCount, repeatInterval, false);

            pipeLine.UseMockup = true;
            pipeLine.Add("repeatTest", info);

            try
            {
                // Act
                await pipeLine.Excute("repeatTest");

                // 반복 작업이 완료될 때까지 대기
                // (실제 상황에서는 이렇게 기다리지 않지만 테스트를 위해 필요)
                await UniTask.Delay((repeatInterval * expectedCallCount) + 1000 * expectedCallCount);

                // Assert
                Assert.AreEqual(expectedCallCount, handlerCallCount, "핸들러 호출 횟수가 예상과 다릅니다");

                // 응답이 순차적으로 처리되었는지 확인
                Assert.AreEqual("pending", receivedResponses[0], "첫 번째 응답이 올바르지 않습니다");
                Assert.AreEqual("processing", receivedResponses[1], "두 번째 응답이 올바르지 않습니다");
                Assert.AreEqual("completed", receivedResponses[2], "세 번째 응답이 올바르지 않습니다");
            }
            finally
            {
                // 테스트 정리
                await pipeLine.RemoveAsync("repeatTest");
                MockHttpRequester.ClearResponses(testUrl);
            }
        }

        /// <summary>
        /// 실행 중인 반복 요청을 중지하는 기능을 테스트합니다.
        /// </summary>
        [Test]
        public async UniTask Test_StopRepeat_StopsExecutionCorrectly()
        {
            // Arrange
            int handlerCallCount = 0;
            string testUrl = "http://test.com/repeat-stop";
            int repeatInterval = 100;

            // Mock 응답 설정
            string mockResponse = @"{""message"": ""Success"", ""data"": {""id"": 2, ""status"": ""running"", ""timestamp"": ""2025-06-09T11:00:00Z""}}";
            MockHttpRequester.SetResponse(testUrl, mockResponse);

            // DataMask와 DataMapper 설정
            var dataMask = new DataMask();
            dataMask["id"] = 0;
            dataMask["status"] = "";
            dataMask["timestamp"] = "";

            var dataMapper = new DataMapper(dataMask);

            // 무한 반복 설정을 포함한 HttpPipeLineInfo 생성
            var info = new HttpPipeLineInfo(testUrl, "get")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) => { handlerCallCount++; })
                .setRepeat(true, 0, repeatInterval, false); // 무한 반복 (repeatCount = 0)

            pipeLine.UseMockup = true;
            pipeLine.Add("infiniteRepeatTest", info);

            try
            {
                // Act
                await pipeLine.Excute("infiniteRepeatTest");

                // 몇 번의 반복 실행이 될 때까지 대기
                await UniTask.Delay(repeatInterval * 2 + 50);

                // 반복 중지
                await pipeLine.StopRepeat("infiniteRepeatTest");

                // 현재 호출 횟수 기록
                int callCountBeforeStop = handlerCallCount;

                // 더 이상 호출이 발생하지 않는지 확인하기 위해 추가 대기
                await UniTask.Delay(repeatInterval * 2 + 50);

                // Assert
                Assert.Greater(callCountBeforeStop, 0, "반복 호출이 발생하지 않았습니다");
                Assert.AreEqual(callCountBeforeStop, handlerCallCount, "반복 중지 후에도 호출이 계속 발생했습니다");
            }
            finally
            {
                // 테스트 정리
                await pipeLine.RemoveAsync("infiniteRepeatTest");
                MockHttpRequester.ClearResponses(testUrl);
            }
        }

        /// <summary>
        /// 여러 개의 반복 요청을 동시에 실행하고 개별적으로 중지하는 기능을 테스트합니다.
        /// </summary>
        [Test]
        public async UniTask Test_MultipleRepeatingRequests_ManagedIndependently()
        {
            // Arrange
            int handlerCallCount1 = 0;
            int handlerCallCount2 = 0;

            string testUrl1 = "http://test.com/repeat1";
            string testUrl2 = "http://test.com/repeat2";

            int repeatInterval1 = 100;
            int repeatInterval2 = 150;

            // Mock 응답 설정
            string mockResponse1 = @"{""message"": ""Success"", ""data"": {""id"": 3, ""name"": ""작업1""}}";
            string mockResponse2 = @"{""message"": ""Success"", ""data"": {""id"": 4, ""name"": ""작업2""}}";

            MockHttpRequester.SetResponse(testUrl1, mockResponse1);
            MockHttpRequester.SetResponse(testUrl2, mockResponse2);

            // DataMask 설정
            var dataMask = new DataMask();
            dataMask["id"] = 0;
            dataMask["name"] = "";

            var dataMapper = new DataMapper(dataMask);

            // 두 개의 반복 요청 설정
            var info1 = new HttpPipeLineInfo(testUrl1, "get")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) => { handlerCallCount1++; })
                .setRepeat(true, 0, repeatInterval1, false);

            var info2 = new HttpPipeLineInfo(testUrl2, "get")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) => { handlerCallCount2++; })
                .setRepeat(true, 0, repeatInterval2, false);

            pipeLine.UseMockup = true;
            pipeLine.Add("repeatTest1", info1);
            pipeLine.Add("repeatTest2", info2);

            try
            {
                // Act
                await pipeLine.Excute("repeatTest1");
                await pipeLine.Excute("repeatTest2");

                // 두 요청 모두 몇 번 실행되도록 대기
                await UniTask.Delay(Math.Max(repeatInterval1, repeatInterval2) * 3 + 100 * 3 + 50);//100 네트워크 지연, 50 추가 여유

                // 첫 번째 반복만 중지
                await pipeLine.StopRepeat("repeatTest1");

                // 호출 횟수 기록
                int callCount1AfterStop = handlerCallCount1;
                int callCount2BeforeSecondStop = handlerCallCount2;

                // 두 번째 반복이 계속 실행되는지 확인하기 위해 대기
                await UniTask.Delay(repeatInterval2 * 2 + 100 * 2 + 50);
                // Assert
                Assert.Greater(callCount1AfterStop, 0, "첫 번째 반복 요청 실행이 발생하지 않았습니다");
                Assert.Greater(handlerCallCount2, callCount2BeforeSecondStop, "두 번째 반복이 계속 실행되지 않았습니다");
                Assert.AreEqual(callCount1AfterStop, handlerCallCount1, "첫 번째 반복이 중지되지 않았습니다");
                // 두 번째 반복도 중지
                await pipeLine.StopRepeat("repeatTest2");
                // 호출 횟수 다시 기록
                int callCount2AfterStop = handlerCallCount2;
                // 더 이상 호출이 없는지 확인하기 위해 대기
                await UniTask.Delay(Math.Max(repeatInterval1, repeatInterval2) * 2 + 100 * 2 + 50);
                // 모든 호출이 중지되었는지 확인
                Assert.AreEqual(callCount1AfterStop, handlerCallCount1, "첫 번째 반복이 계속되었습니다");
                Assert.AreEqual(callCount2AfterStop, handlerCallCount2, "두 번째 반복이 계속되었습니다");
            }
            finally
            {
                // 테스트 정리
                await pipeLine.RemoveAsync("repeatTest1");
                await pipeLine.RemoveAsync("repeatTest2");
                MockHttpRequester.ClearResponses(testUrl1);
                MockHttpRequester.ClearResponses(testUrl2);
            }
        }

        /// <summary>
        /// 지정된 횟수만큼 반복 실행 후 자동 중단되는 기능을 테스트합니다.
        /// </summary>
        [Test]
        public async UniTask Test_RepeatWithCount_StopsAutomatically()
        {
            // Arrange
            int handlerCallCount = 0;
            List<IDataObject?> receivedData = new List<IDataObject?>();

            string testUrl = "http://test.com/repeat-count";
            int repeatCount = 3;
            int repeatInterval = 100;

            // Mock 응답 설정
            string mockResponse = @"{""message"": ""Success"", ""data"": {""id"": 5, ""message"": ""자동 중단 테스트""}}";
            MockHttpRequester.SetResponse(testUrl, mockResponse);

            // DataMask 설정
            var dataMask = new DataMask();
            dataMask["id"] = 0;
            dataMask["message"] = "";

            var dataMapper = new DataMapper(dataMask);

            // 반복 횟수가 지정된 HttpPipeLineInfo 생성
            var info = new HttpPipeLineInfo(testUrl, "get")
                .setDataMapper(dataMapper)
                .setSuccessHandler((data) =>
                {
                    handlerCallCount++;
                    receivedData.Add(data);
                })
                .setRepeat(true, repeatCount, repeatInterval, false);

            pipeLine.UseMockup = true;
            pipeLine.Add("countedRepeatTest", info);

            try
            {
                // Act
                await pipeLine.Excute("countedRepeatTest");

                // 지정된 횟수의 반복이 완료될 때까지 충분히 대기
                await UniTask.Delay(repeatInterval * (repeatCount + 1) + 50 + 100 * (repeatCount + 1));

                // Assert
                Assert.AreEqual(repeatCount, handlerCallCount, "지정된 횟수만큼 반복 실행되지 않았습니다");
                Assert.AreEqual(repeatCount, receivedData.Count, "기대한 데이터 수와 다릅니다");

                // 각 응답이 올바르게 처리되었는지 확인
                var data = receivedData[0];
                Assert.IsNotNull(data, "데이터가 null입니다");
                var dataObject = data as DataObject;
                Assert.AreEqual(5, dataObject?.GetInt("id"), "ID가 올바르지 않습니다");
                Assert.AreEqual("자동 중단 테스트", dataObject?.GetString("message"), "메시지가 올바르지 않습니다");

                // 자동으로 제거되었는지 확인
                var repeatTokenSources = GetRepeatTokenSourcesField();
                Assert.IsFalse(repeatTokenSources.ContainsKey("countedRepeatTest"),
                    "실행 완료 후 반복 토큰이 제거되지 않았습니다");
            }
            finally
            {
                // 테스트 정리
                await pipeLine.RemoveAsync("countedRepeatTest");
                MockHttpRequester.ClearResponses(testUrl);
            }
        }

        /// <summary>
        /// HttpPipeLine의 private repeatTokenSources 필드 가져오기
        /// </summary>
        private Dictionary<string, CancellationTokenSource> GetRepeatTokenSourcesField()
        {
            var fieldInfo = typeof(HttpPipeLine).GetField("repeatTokenSources",
                BindingFlags.NonPublic | BindingFlags.Instance);

            return (Dictionary<string, CancellationTokenSource>)fieldInfo.GetValue(pipeLine);
        }

        #region HttpResponseMask Tests

        /// <summary>
        /// HttpResponseMask.Apply가 성공적인 응답을 올바르게 처리하는지 테스트합니다.
        /// </summary>
        [Test]
        public void HttpResponseMask_Apply_SuccessfulResponse_ReturnsSuccessWithData()
        {
            // Arrange
            var responseMask = new HttpResponseMask(); // 기본값 사용 (successKey="message", successValue="success", dataKey="data")
            var jsonResponse = @"{""message"": ""success"", ""data"": {""key"":""value""}}";
            var expectedData = @"{""key"":""value""}";

            // Act
            var result = responseMask.Apply(jsonResponse);

            // Assert
            Assert.IsTrue(result.IsSuccess, "결과가 성공이어야 합니다.");
            Assert.AreEqual(expectedData, result.Data, "추출된 데이터가 예상과 다릅니다.");
            Assert.IsNull(result.Message, "성공 시 메시지는 null이어야 합니다.");
        }

        /// <summary>
        /// HttpResponseMask.Apply가 잘못된 성공 값으로 실패 응답을 올바르게 처리하는지 테스트합니다.
        /// </summary>
        [Test]
        public void HttpResponseMask_Apply_FailedResponse_WrongSuccessValue_ReturnsFailWithMessage()
        {
            // Arrange
            var responseMask = new HttpResponseMask();
            var jsonResponse = @"{""message"": ""failed"", ""data"": {""key"": ""value""}}"; // successValue가 "success"가 아님

            // Act
            var result = responseMask.Apply(jsonResponse);

            // Assert
            Assert.IsFalse(result.IsSuccess, "결과가 실패여야 합니다.");
            Assert.IsNull(result.Data, "실패 시 데이터는 null이어야 합니다.");
            Assert.AreEqual(jsonResponse, result.Message, "실패 시 메시지는 원본 응답이어야 합니다.");
        }

        /// <summary>
        /// HttpResponseMask.Apply가 성공 키가 없는 실패 응답을 올바르게 처리하는지 테스트합니다.
        /// </summary>
        [Test]
        public void HttpResponseMask_Apply_FailedResponse_MissingSuccessKey_ReturnsFailWithMessage()
        {
            // Arrange
            var responseMask = new HttpResponseMask();
            var jsonResponse = @"{""error"": ""some error"", ""data"": {""key"": ""value""}}"; // "message" 키가 없음

            // Act
            var result = responseMask.Apply(jsonResponse);

            // Assert
            Assert.IsFalse(result.IsSuccess, "결과가 실패여야 합니다.");
            Assert.IsNull(result.Data, "실패 시 데이터는 null이어야 합니다.");
            Assert.AreEqual(jsonResponse, result.Message, "실패 시 메시지는 원본 응답이어야 합니다.");
        }

        /// <summary>
        /// HttpResponseMask.Apply가 데이터 키가 없는 실패 응답을 올바르게 처리하는지 테스트합니다.
        /// (현재 구현상 successKey 조건만 만족하면 dataKey가 없어도 성공으로 간주하고 Data를 null로 반환할 수 있으므로, 이 테스트는 실패할 수 있습니다.
        ///  요구사항에 따라 이 부분의 동작을 명확히 하고 테스트를 조정해야 합니다.)
        /// </summary>
        [Test]
        public void HttpResponseMask_Apply_FailedResponse_MissingDataKey_ReturnsFailWithMessage()
        {
            // Arrange
            var responseMask = new HttpResponseMask();
            // 성공 키는 있지만 데이터 키가 없는 경우
            var jsonResponse = @"{""message"": ""success"", ""payload"": {""key"": ""value""}}"; // "data" 키가 없음

            // Act
            var result = responseMask.Apply(jsonResponse);

            // Assert
            // 현재 Apply 메서드 구현에 따르면, successKey/successValue가 일치하고 dataKey가 없으면
            // IsSuccess = false, Data = null 로 반환됩니다.
            // 만약 dataKey가 필수라면 Apply 메서드 수정 또는 이 테스트의 기대 결과 수정이 필요합니다.
            // 여기서는 현재 구현을 기준으로 테스트합니다.
            Assert.IsFalse(result.IsSuccess, "data 키가 없으면 success 조건 만족 시 실패여야 합니다 (현재 로직 기준).");
            Assert.IsNull(result.Data, "data 키가 없는 경우 Data는 null이어야 합니다.");
            Assert.IsNotNull(result.Message, "실패 시 메시지는 null이 아니어야 합니다.");
        }

        /// <summary>
        /// HttpResponseMask.Apply가 사용자 정의 키를 사용하여 성공적인 응답을 올바르게 처리하는지 테스트합니다.
        /// </summary>
        [Test]
        public void HttpResponseMask_Apply_SuccessfulResponse_CustomKeys_ReturnsSuccessWithData()
        {
            // Arrange
            var responseMask = new HttpResponseMask("status", "ok", "payload");
            var jsonResponse = @"{""status"": ""ok"", ""payload"": {""info"":""custom data""}}";
            var expectedData = @"{""info"":""custom data""}";

            // Act
            var result = responseMask.Apply(jsonResponse);

            // Assert
            Assert.IsTrue(result.IsSuccess, "결과가 성공이어야 합니다.");
            Assert.AreEqual(expectedData, result.Data, "추출된 데이터가 예상과 다릅니다.");
            Assert.IsNull(result.Message, "성공 시 메시지는 null이어야 합니다.");
        }

        /// <summary>
        /// HttpResponseMask.Apply가 잘못된 JSON 형식의 응답을 처리할 때 예외를 발생하는지 테스트합니다.
        /// </summary>
        [Test]
        public void HttpResponseMask_Apply_InvalidJson_ThrowsException()
        {
            // Arrange
            var responseMask = new HttpResponseMask();
            var invalidJsonResponse = @"{""message"": ""success"", ""data"": {""key"": ""value"""; // 닫는 중괄호 누락

            // Act & Assert
            Assert.Throws<Newtonsoft.Json.JsonReaderException>(() => responseMask.Apply(invalidJsonResponse), "잘못된 JSON 형식에 대해 JsonReaderException이 발생해야 합니다.");
        }

        #endregion
    }
}