using Newtonsoft.Json.Linq;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using UnityEngine;
using UVC.Data;

namespace UVC.Tests.Data
{
    /// <summary>
    /// DataMapper 클래스의 테스트를 위한 테스트 클래스입니다.
    /// </summary>
    [TestFixture]
    public class DataMapperTests
    {
        /// <summary>
        /// 모든 테스트 메서드를 실행하는 메서드입니다.
        /// </summary>
        /// <remarks>
        /// 이 메서드는 클래스의 모든 테스트 메서드를 순차적으로 호출하고 
        /// 각 테스트의 성공 또는 실패 여부를 로그로 출력합니다.
        /// </remarks>
        public void TestAll()
        {
            Debug.Log("===== DataMapper 테스트 시작 =====");

            RunTest(nameof(Map_StringProperty_MapsCorrectly), Map_StringProperty_MapsCorrectly);
            RunTest(nameof(Map_IntProperty_MapsCorrectly), Map_IntProperty_MapsCorrectly);
            RunTest(nameof(Map_DoubleProperty_MapsCorrectly), Map_DoubleProperty_MapsCorrectly);
            RunTest(nameof(Map_BoolProperty_MapsCorrectly), Map_BoolProperty_MapsCorrectly);
            RunTest(nameof(Map_DateTimeProperty_MapsCorrectly), Map_DateTimeProperty_MapsCorrectly);
            RunTest(nameof(Map_DataMapProperty_MapsCorrectly), Map_DataMapProperty_MapsCorrectly);
            RunTest(nameof(Map_DataMapWithUnmappedValue_ReturnsOriginal), Map_DataMapWithUnmappedValue_ReturnsOriginal);
            RunTest(nameof(Map_EnumProperty_MapsCorrectly), Map_EnumProperty_MapsCorrectly);
            RunTest(nameof(Map_AdditionalProperty_AddsToResult), Map_AdditionalProperty_AddsToResult);
            RunTest(nameof(Map_InvalidDateTimeString_ReturnsNull), Map_InvalidDateTimeString_ReturnsNull);
            RunTest(nameof(Map_ComplexObject_MapsAllProperties), Map_ComplexObject_MapsAllProperties);
            RunTest(nameof(Map_NestedObject_MapsCorrectly), Map_NestedObject_MapsCorrectly);
            RunTest(nameof(Map_ArrayMapping_MapsCorrectly), Map_ArrayMapping_MapsCorrectly);
            RunTest(nameof(Map_EmptyMaskArray_CopiesSourceArray), Map_EmptyMaskArray_CopiesSourceArray);
            RunTest(nameof(Map_ComplexNestedStructure_MapsCorrectly), Map_ComplexNestedStructure_MapsCorrectly);
            RunTest(nameof(Map_MixedArrayTypes_HandlesCorrectly), Map_MixedArrayTypes_HandlesCorrectly);
            RunTest(nameof(Map_WithNamesForReplace_ShouldRenameProperties), Map_WithNamesForReplace_ShouldRenameProperties);
            RunTest(nameof(Map_DeepClone_CopiesAllProperties), Map_DeepClone_CopiesAllProperties);
            RunTest(nameof(Map_ToJObject_ConvertsCorrectly), Map_ToJObject_ConvertsCorrectly);
            RunTest(nameof(Map_ParallelArrayProcessing_WorksCorrectly), Map_ParallelArrayProcessing_WorksCorrectly);
            RunTest(nameof(Map_RecursionDepthLimit_HandlesCorrectly), Map_RecursionDepthLimit_HandlesCorrectly);

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

        /// <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}");
            }
        }

        /// <summary>
        /// 문자열 타입 매핑 테스트
        /// </summary>
        [Test]
        public void Map_StringProperty_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask();
            mask["Name"] = "홍길동";
            mask.ObjectIdKey = "Name"; // Id로 사용할 키 설정

            var source = JObject.Parse(@"{ ""Name"": ""김철수"" }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("Name"));
            Assert.AreEqual("김철수", result.GetString("Name"));
            Assert.AreEqual("김철수", result.Id);
        }

        /// <summary>
        /// 정수 타입 매핑 테스트
        /// </summary>
        [Test]
        public void Map_IntProperty_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask();
            mask["Age"] = 25;
            mask.ObjectIdKey = "Age"; // Id로 사용할 키 설정

            var source = JObject.Parse(@"{ ""Age"": 30 }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("Age"));
            Assert.AreEqual(30, result.GetInt("Age"));
            Assert.AreEqual("30", result.Id);
        }

        /// <summary>
        /// 부동소수점 타입 매핑 테스트
        /// </summary>
        [Test]
        public void Map_DoubleProperty_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask();
            mask["Height"] = 175.5;
            mask.ObjectIdKey = "Height"; // Id로 사용할 키 설정

            var source = JObject.Parse(@"{ ""Height"": 180.5 }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("Height"));
            Assert.AreEqual(180.5, result.GetDouble("Height"));
            Assert.AreEqual("180.5", result.Id);
        }

        /// <summary>
        /// Boolean 타입 매핑 테스트
        /// </summary>
        [Test]
        public void Map_BoolProperty_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask();
            mask["IsActive"] = false;

            var source = JObject.Parse(@"{ ""IsActive"": true }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("IsActive"));
            Assert.IsTrue(result.GetBool("IsActive"));
            Assert.AreEqual("True", result.Id);// Id는 Boolean 값의 문자열 표현으로 설정됨(첫글자 대문자)
        }

        /// <summary>
        /// DateTime 타입 매핑 테스트
        /// </summary>
        [Test]
        public void Map_DateTimeProperty_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask();
            mask["BirthDate"] = JToken.FromObject(DateTime.Now);

            var expectedDate = new DateTime(1990, 1, 1);
            var source = JObject.Parse(@"{ ""BirthDate"": ""1990-01-01T00:00:00.000"" }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("BirthDate"));
            Assert.AreEqual(expectedDate, result.GetDateTime("BirthDate"));
        }

        /// <summary>
        /// DataMap 타입 매핑 테스트
        /// </summary>
        [Test]
        public void Map_DataMapProperty_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask();
            var valueMapper = new DataMap();
            valueMapper["ON"] = "활성화";
            valueMapper["OFF"] = "비활성화";
            mask["Status"] = JToken.FromObject(valueMapper);

            var source = JObject.Parse(@"{ ""Status"": ""ON"" }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("Status"));
            Assert.AreEqual("활성화", result.GetString("Status"));
        }

        /// <summary>
        /// DataMap에 매핑되지 않는 값 처리 테스트
        /// </summary>
        [Test]
        public void Map_DataMapWithUnmappedValue_ReturnsOriginal()
        {
            // Arrange
            var mask = new DataMask();
            var valueMapper = new DataMap();
            valueMapper["ON"] = "활성화";
            valueMapper["OFF"] = "비활성화";
            mask["Status"] = JToken.FromObject(valueMapper);

            var source = JObject.Parse(@"{ ""Status"": ""UNKNOWN"" }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("Status"));
            Assert.AreEqual("UNKNOWN", result.GetString("Status"));
        }

        /// <summary>
        /// 열거형 타입 매핑 테스트
        /// </summary>
        [Test]
        public void Map_EnumProperty_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask();
            mask["Status"] = JToken.FromObject(UserStatus.Inactive);

            var source = JObject.Parse(@"{ ""Status"": ""Active"" }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("Status"));
            Assert.AreEqual(UserStatus.Active, result.GetEnum<UserStatus>("Status"));
        }

        /// <summary>
        /// 타겟에 없는 추가 속성 매핑 테스트
        /// </summary>
        [Test]
        public void Map_AdditionalProperty_AddsToResult()
        {
            // Arrange
            var mask = new DataMask();
            mask["Name"] = "홍길동";

            var source = JObject.Parse(@"{ ""Name"": ""김철수"", ""Email"": ""kim@example.com"" }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsFalse(result.ContainsKey("Email"));
            Assert.AreEqual(null, result.GetString("Email"));
        }

        /// <summary>
        /// 유효하지 않은 DateTime 문자열 매핑 테스트
        /// </summary>
        [Test]
        public void Map_InvalidDateTimeString_ReturnsNull()
        {
            // Arrange
            var mask = new DataMask();
            mask["BirthDate"] = JToken.FromObject(DateTime.Now);

            var source = JObject.Parse(@"{ ""BirthDate"": ""InvalidDateTime"" }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("BirthDate"));
            Assert.IsNull(result["BirthDate"]);
        }

        /// <summary>
        /// 복합 객체 매핑 테스트
        /// </summary>
        [Test]
        public void Map_ComplexObject_MapsAllProperties()
        {
            // Arrange
            var mask = new DataMask();
            mask["Id"] = 1;
            mask["Name"] = "홍길동";
            mask["IsActive"] = false;
            mask["JoinDate"] = JToken.FromObject(DateTime.Now);
            mask["Height"] = 175.5;
            mask["Status"] = JToken.FromObject(UserStatus.Inactive);

            string json = @"{
                ""Id"": 100,
                ""Name"": ""김철수"",
                ""IsActive"": true,
                ""JoinDate"": ""2023-01-01T00:00:00"",
                ""Height"": 180.0,
                ""Status"": ""Active"",
                ""Email"": ""kim@example.com""
            }";

            var source = JObject.Parse(json);
            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.AreEqual(6, result.Count);
            Assert.AreEqual(100, result.GetInt("Id"));
            Assert.AreEqual("김철수", result.GetString("Name"));
            Assert.IsTrue(result.GetBool("IsActive"));
            Assert.AreEqual(new DateTime(2023, 1, 1), result.GetDateTime("JoinDate"));
            Assert.AreEqual(180.0, result.GetDouble("Height"));
            Assert.AreEqual(UserStatus.Active, result.GetEnum<UserStatus>("Status"));
            Assert.AreEqual(null, result.GetString("Email"));
        }

        /// <summary>
        /// 중첩된 객체 매핑 테스트
        /// </summary>
        [Test]
        public void Map_NestedObject_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask
            {
                ["User"] = new DataMask
                {
                    ["Name"] = "홍길동",
                    ["Address"] = new DataMask
                    {
                        ["City"] = "서울",
                        ["ZipCode"] = "12345"
                    }
                }
            };

            var source = JObject.Parse(@"{
                ""User"": {
                    ""Name"": ""김철수"",
                    ""Address"": {
                        ""City"": ""부산"",
                        ""ZipCode"": ""67890"",
                        ""Country"": ""대한민국""
                    }
                }
            }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("User"));

            var user = result.GetDataObject("User");
            Assert.IsNotNull(user);
            Assert.AreEqual("김철수", user.GetString("Name"));

            var address = user.GetDataObject("Address");
            Assert.IsNotNull(address);
            Assert.AreEqual("부산", address.GetString("City"));
            Assert.AreEqual("67890", address.GetString("ZipCode"));
            Assert.AreEqual(null, address.GetString("Country"));
        }

        /// <summary>
        /// 배열 매핑 테스트
        /// </summary>
        [Test]
        public void Map_ArrayMapping_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask
            {
                ["Contacts"] = new List<DataMask>
                {
                    new DataMask
                    {
                        ["Type"] = "mobile",
                        ["Number"] = "010-0000-0000"
                    }
                }
            };

            var source = JObject.Parse(@"{
                ""Contacts"": [
                    { ""Type"": ""mobile"", ""Number"": ""010-1234-5678"" },
                    { ""Type"": ""home"", ""Number"": ""02-123-4567"", ""Extension"": ""123"" }
                ]
            }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("Contacts"));
            var contacts = result.GetDataArray("Contacts");
            Assert.IsNotNull(contacts);
            Assert.AreEqual(2, contacts.Count);

            var contact1 = contacts[0];
            Assert.IsNotNull(contact1);
            Assert.AreEqual("mobile", contact1.GetString("Type"));
            Assert.AreEqual("010-1234-5678", contact1.GetString("Number"));

            var contact2 = contacts[1];
            Assert.IsNotNull(contact2);
            Assert.AreEqual("home", contact2.GetString("Type"));
            Assert.AreEqual("02-123-4567", contact2.GetString("Number"));
            Assert.AreEqual(null, contact2.GetString("Extension"));
        }

        /// <summary>
        /// 빈 가이드 배열 테스트
        /// </summary>
        [Test]
        public void Map_EmptyMaskArray_CopiesSourceArray()
        {
            // Arrange
            var mask = new DataMask
            {
                ["Tags"] = new List<DataMask>()
            };

            var source = JObject.Parse(@"{
                ""Tags"": [""개발"", ""테스트"", ""배포""]
            }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("Tags"));
            var tags = result.GetDataArray("Tags");
            Assert.IsNotNull(tags);
            Assert.AreEqual(3, tags.Count);

            // DataArray의 항목들을 검증
            for (int i = 0; i < tags.Count; i++)
            {
                var item = tags[i];
                Assert.IsNotNull(item);
                // DataObject는 "value" 키에 실제 값을 저장
                Assert.AreEqual((source["Tags"])[i].ToString(), item.GetString("value"));
            }
        }

        /// <summary>
        /// 복잡한 중첩 구조 매핑 테스트
        /// </summary>
        [Test]
        public void Map_ComplexNestedStructure_MapsCorrectly()
        {
            // Arrange
            var mask = new DataMask
            {
                ["Company"] = new DataMask
                {
                    ["Name"] = "회사명",
                    ["Founded"] = JToken.FromObject(DateTime.Now),
                    ["Departments"] = new List<DataMask>
                    {
                        new DataMask
                        {
                            ["Name"] = "부서명",
                            ["Employees"] = new List<DataMask>
                            {
                                new DataMask
                                {
                                    ["Name"] = "직원명",
                                    ["Age"] = 30,
                                    ["Status"] = JToken.FromObject(UserStatus.Active)
                                }
                            }
                        }
                    }
                }
            };

            var source = JObject.Parse(@"{
                ""Company"": {
                    ""Name"": ""XYZ 주식회사"",
                    ""Founded"": ""2000-01-01T00:00:00"",
                    ""Departments"": [
                        {
                            ""Name"": ""개발부"",
                            ""Employees"": [
                                { ""Name"": ""김개발"", ""Age"": 35, ""Status"": ""Active"" },
                                { ""Name"": ""이테스트"", ""Age"": 28, ""Status"": ""Inactive"" }
                            ]
                        },
                        {
                            ""Name"": ""마케팅부"",
                            ""Employees"": [
                                { ""Name"": ""박마케팅"", ""Age"": 32, ""Status"": ""Active"" }
                            ],
                            ""Budget"": 500000
                        }
                    ],
                    ""Address"": ""서울시 강남구""
                }
            }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            var company = result.GetDataObject("Company");
            Assert.IsNotNull(company);
            Assert.AreEqual("XYZ 주식회사", company.GetString("Name"));
            Assert.AreEqual(new DateTime(2000, 1, 1), company.GetDateTime("Founded"));
            Assert.AreEqual(null, company.GetString("Address"));

            var departments = company.GetDataArray("Departments");
            Assert.IsNotNull(departments);
            Assert.AreEqual(2, departments.Count);

            var devDept = departments[0];
            Assert.AreEqual("개발부", devDept.GetString("Name"));
            var devEmployees = devDept.GetDataArray("Employees");
            Assert.AreEqual(2, devEmployees.Count);
            Assert.AreEqual("김개발", devEmployees[0].GetString("Name"));
            Assert.AreEqual(35, devEmployees[0].GetInt("Age"));
            Assert.AreEqual(UserStatus.Active, devEmployees[0].GetEnum<UserStatus>("Status"));
            Assert.AreEqual("이테스트", devEmployees[1].GetString("Name"));
            Assert.AreEqual(UserStatus.Inactive, devEmployees[1].GetEnum<UserStatus>("Status"));

            var marketingDept = departments[1];
            Assert.AreEqual("마케팅부", marketingDept.GetString("Name"));
            Assert.AreEqual(0, marketingDept.GetInt("Budget"));
            var marketingEmployees = marketingDept.GetDataArray("Employees");
            Assert.AreEqual(1, marketingEmployees.Count);
            Assert.AreEqual("박마케팅", marketingEmployees[0].GetString("Name"));
        }

        /// <summary>
        /// 다양한 형식의 배열 테스트
        /// </summary>
        [Test]
        public void Map_MixedArrayTypes_HandlesCorrectly()
        {
            // Arrange
            var mask = new DataMask(@"{
                ""MixedArray"": [
                    { ""Key"": ""Value"", ""Key1"": 1, ""Key2"": true},
                ]
            }");

            var source = JObject.Parse(@"{
                ""MixedArray"": [
                    { ""Key"": ""NewValue"", ""Key1"": 456, ""Key2"": false, ""Extra"": ""ExtraValue"" },
                    { ""items"": [1, 2, 3] }
                ]
            }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("MixedArray"));
            var mixedArray = result.GetDataArray("MixedArray");
            Assert.IsNotNull(mixedArray);
            Assert.AreEqual(2, mixedArray.Count);

            // 문자열 항목 검증
            Assert.AreEqual("NewValue", mixedArray[0].GetString("Key"));
            // 숫자 항목 검증
            Assert.AreEqual(456, mixedArray[0].GetInt("Key1"));
            // 불리언 항목 검증
            Assert.AreEqual(false, mixedArray[0].GetBool("Key2"));

            // 객체 항목 검증
            var objInArray = mixedArray[0];
            Assert.IsNotNull(objInArray);
            Assert.AreEqual("NewValue", objInArray.GetString("Key"));
        }


        /// <summary>
        /// NamesForReplace 속성이 제대로 구현되었는지 테스트합니다.
        /// </summary>
        [Test]
        public void Map_WithNamesForReplace_ShouldRenameProperties()
        {
            // Arrange
            var mask = new DataMask();
            mask["OldName"] = "홍길동";
            mask["Age"] = 25;
            mask.NamesForReplace = new Dictionary<string, string> {
                { "OldName", "NewName" }
            };

            var source = JObject.Parse(@"{ ""OldName"": ""김철수"", ""Age"": 30 }");

            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            Assert.IsTrue(result.ContainsKey("NewName"));
            Assert.AreEqual("김철수", result.GetString("NewName"));
            Assert.IsFalse(result.ContainsKey("OldName"));
            Assert.IsTrue(result.ContainsKey("Age"));
            Assert.AreEqual(30, result.GetInt("Age"));
        }

        /// <summary>
        /// DataMask의 DeepClone 메서드가 모든 속성을 올바르게 복사하는지 테스트합니다.
        /// </summary>
        [Test]
        public void Map_DeepClone_CopiesAllProperties()
        {
            // Arrange
            var originalMask = new DataMask();
            originalMask["Name"] = "홍길동";
            originalMask["Age"] = 25;
            originalMask.ObjectIdKey = "Name";
            originalMask.ObjectName = "Person";
            originalMask.NamesForReplace = new Dictionary<string, string> {
                { "full_name", "Name" },
                { "user_age", "Age" }
            };

            // Act
            var clonedMask = originalMask.DeepClone();

            // 원본 마스크 수정
            originalMask["Name"] = "변경된 이름";
            originalMask.ObjectName = "ModifiedPerson";
            originalMask.NamesForReplace["full_name"] = "FullName";

            // Assert - 클론은 변경되지 않아야 함
            Assert.AreEqual("홍길동", clonedMask["Name"]);
            Assert.AreEqual(25, clonedMask["Age"]);
            Assert.AreEqual("Name", clonedMask.ObjectIdKey);
            Assert.AreEqual("Person", clonedMask.ObjectName);
            Assert.AreEqual("Name", clonedMask.NamesForReplace["full_name"]);
            Assert.AreEqual("Age", clonedMask.NamesForReplace["user_age"]);

            // 원본 마스크는 변경되어야 함
            Assert.AreEqual("변경된 이름", originalMask["Name"]);
            Assert.AreEqual("ModifiedPerson", originalMask.ObjectName);
            Assert.AreEqual("FullName", originalMask.NamesForReplace["full_name"]);
        }

        /// <summary>
        /// DataMask의 ToJObject 메서드가 올바르게 동작하는지 테스트합니다.
        /// </summary>
        [Test]
        public void Map_ToJObject_ConvertsCorrectly()
        {
            // Arrange
            var mask = new DataMask();
            mask["Name"] = "홍길동";
            mask["Age"] = 25;
            mask["IsActive"] = true;
            mask["Height"] = 175.5;

            // Act
            var jObject = mask.ToJObject();

            // Assert
            Assert.IsNotNull(jObject);
            Assert.AreEqual(4, jObject.Count);
            Assert.AreEqual("홍길동", jObject["Name"].ToString());
            Assert.AreEqual(25, jObject["Age"].ToObject<int>());
            Assert.AreEqual(true, jObject["IsActive"].ToObject<bool>());
            Assert.AreEqual(175.5, jObject["Height"].ToObject<double>());
        }

        /// <summary>
        /// DataMapper의 병렬 처리 기능이 대용량 배열에서 올바르게 동작하는지 테스트합니다.
        /// </summary>
        [Test]
        public void Map_ParallelArrayProcessing_WorksCorrectly()
        {
            // Arrange
            var mask = new DataMask();
            mask["Items"] = new List<DataMask> { new DataMask { ["Id"] = 0, ["Name"] = "" } };

            // 2000개 아이템을 가진 소스 배열 생성
            var itemsArray = new JArray();
            for (int i = 1; i <= 2000; i++)
            {
                itemsArray.Add(new JObject
                {
                    ["Id"] = i,
                    ["Name"] = $"Item {i}"
                });
            }

            var source = new JObject { ["Items"] = itemsArray };
            var mapper = new DataMapper(mask);

            // Act
            var result = mapper.Map(source);

            // Assert
            var items = result.GetDataArray("Items");
            Assert.IsNotNull(items);
            Assert.AreEqual(2000, items.Count);

            // 결과 검증 - 몇 개 아이템만 샘플링
            Assert.AreEqual(1, items[0].GetInt("Id"));
            Assert.AreEqual("Item 1", items[0].GetString("Name"));

            Assert.AreEqual(1000, items[999].GetInt("Id"));
            Assert.AreEqual("Item 1000", items[999].GetString("Name"));

            Assert.AreEqual(2000, items[1999].GetInt("Id"));
            Assert.AreEqual("Item 2000", items[1999].GetString("Name"));
        }

        /// <summary>
        /// 재귀 깊이 제한이 올바르게 동작하는지 테스트합니다.
        /// </summary>
        [Test]
        public void Map_RecursionDepthLimit_HandlesCorrectly()
        {
            // Arrange - 매우 깊게 중첩된 JSON 객체 생성
            var mask = CreateDeepNestedMask(20);
            var source = CreateDeepNestedJObject(20);

            var mapper = new DataMapper(mask);

            string maskJsonString = mask.ToJsonString();

            // Act - 예외 없이 매핑이 수행되어야 함
            var result = mapper.Map(source);

            // Assert - 최대 중첩 깊이(현재 10)까지 매핑된 구조 확인
            var level1 = result.GetDataObject("Level20");
            Assert.IsNotNull(level1);

            var level5 = level1.GetDataObject("Level15")
                              .GetDataObject("Level14")
                              .GetDataObject("Level13")
                              .GetDataObject("Level12");
            Assert.IsNotNull(level5);

            // 깊이 제한 이후 원본 객체를 그대로 반환하므로
            // 원래 깊이보다 더 깊게 들어갈 수 있어야 함
            var level10 = level5.GetDataObject("Level10")
                              .GetDataObject("Level9")
                              .GetDataObject("Level8")
                              .GetDataObject("Level7")
                              .GetDataObject("Level6");
            Assert.IsNotNull(level10);
        }

        /// <summary>
        /// 테스트용 깊게 중첩된 DataMask 생성
        /// </summary>
        private DataMask CreateDeepNestedMask(int depth)
        {
            if (depth <= 0) return new DataMask { ["Value"] = "마스크 값" };

            var mask = new DataMask();
            mask[$"Level{depth}"] = CreateDeepNestedMask(depth - 1);
            return mask;
        }

        /// <summary>
        /// 테스트용 깊게 중첩된 JObject 생성
        /// </summary>
        private JObject CreateDeepNestedJObject(int depth)
        {
            if (depth <= 0) return new JObject { ["Value"] = "소스 값" };

            var obj = new JObject();
            obj[$"Level{depth}"] = CreateDeepNestedJObject(depth - 1);
            return obj;
        }
    }

    /// <summary>
    /// 테스트에 사용할 샘플 열거형
    /// </summary>
    public enum UserStatus
    {
        Active,
        Inactive,
        Suspended
    }
}