using AyCode.Core.Enums;
using AyCode.Core.Extensions;
using AyCode.Core.Interfaces;
using AyCode.Core.Loggers;
using AyCode.Services.SignalRs;
using AyCode.Utils.Extensions;
using FruitBank.Common;
using FruitBank.Common.Dtos;
using FruitBank.Common.Loggers;
using FruitBankHybrid.Shared.Services.SignalRs;
using FruitBankHybrid.Shared.Tests;
using Microsoft.Extensions.Options;
using Mono.Cecil;
using Newtonsoft.Json;
using Nop.Core.Domain.Orders;
using System.Runtime.Serialization;
using AyCode.Core.Serializers.Jsons;

namespace FruitBankHybrid.Shared.Tests;

#region Test Models for Hybrid Reference Test

/// <summary>
/// Level 1 - Root entity implementing IId&lt;int&gt; (uses semantic ID: "Company_1")
/// </summary>
public class Company : IId<int>
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    
    // Collection of IId<int> items
    public List<Department> Departments { get; set; } = new();
    
    // Non-IId object (uses numeric ID)
    public Address HeadquartersAddress { get; set; } = new();
    
    // Array of IId<int> items
    public Employee[] BoardMembers { get; set; } = Array.Empty<Employee>();
}

/// <summary>
/// Level 2 - Department implementing IId&lt;int&gt; (uses semantic ID: "Department_1")
/// </summary>
public class Department : IId<int>
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    
    // Back-reference to parent (should use $ref to Company semantic ID)
    public Company? ParentCompany { get; set; }
    
    // Collection of IId<int> items
    public List<Employee> Employees { get; set; } = new();
    
    // Non-IId object (uses numeric ID)
    public Address? OfficeAddress { get; set; }
    
    // Array of IId<Guid> items
    public Project[] ActiveProjects { get; set; } = Array.Empty<Project>();
}

/// <summary>
/// Level 3 - Employee implementing IId&lt;int&gt; (uses semantic ID: "Employee_1")
/// </summary>
public class Employee : IId<int>
{
    public int Id { get; set; }
    public string FullName { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    
    // Back-reference to department (should use $ref to Department semantic ID)
    public Department? Department { get; set; }
    
    // Non-IId object (uses numeric ID)
    public ContactInfo? Contact { get; set; }
    
    // Collection of IId<Guid> items
    public List<Project> AssignedProjects { get; set; } = new();
    
    // Collection of non-IId items (uses numeric IDs)
    public List<Skill> Skills { get; set; } = new();
}

/// <summary>
/// Level 4 - Project implementing IId&lt;Guid&gt; (uses semantic ID: "Project_guid")
/// </summary>
public class Project : IId<Guid>
{
    public Guid Id { get; set; }
    public string ProjectName { get; set; } = string.Empty;
    public DateTime StartDate { get; set; }
    
    // Back-reference to department (should use $ref to Department semantic ID)
    public Department? OwningDepartment { get; set; }
    
    // Collection of IId<int> - circular reference to employees
    public List<Employee> TeamMembers { get; set; } = new();
    
    // Array of non-IId items
    public Milestone[] Milestones { get; set; } = Array.Empty<Milestone>();
    
    // Collection of IId<long> items
    public List<ProjectTask> Tasks { get; set; } = new();
}

/// <summary>
/// Level 5 - ProjectTask implementing IId&lt;long&gt; (uses semantic ID: "ProjectTask_1")
/// Renamed from Task to avoid conflict with System.Threading.Tasks.Task
/// </summary>
public class ProjectTask : IId<long>
{
    public long Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public bool IsCompleted { get; set; }
    
    // Back-reference to project (should use $ref to Project semantic ID)
    public Project? ParentProject { get; set; }
    
    // Reference to employee (should use $ref to Employee semantic ID)
    public Employee? AssignedTo { get; set; }
    
    // Non-IId object
    public TaskMetadata? Metadata { get; set; }
}

/// <summary>
/// Non-IId class - Address (uses standard numeric $id)
/// </summary>
public class Address
{
    public string Street { get; set; } = string.Empty;
    public string City { get; set; } = string.Empty;
    public string Country { get; set; } = string.Empty;
    public string PostalCode { get; set; } = string.Empty;
}

/// <summary>
/// Non-IId class - ContactInfo (uses standard numeric $id)
/// </summary>
public class ContactInfo
{
    public string Phone { get; set; } = string.Empty;
    public string Mobile { get; set; } = string.Empty;
    
    // Shared address reference (should use $ref to numeric ID)
    public Address? HomeAddress { get; set; }
}

/// <summary>
/// Non-IId class - Skill (uses standard numeric $id)
/// </summary>
public class Skill
{
    public string Name { get; set; } = string.Empty;
    public int Level { get; set; }
}

/// <summary>
/// Non-IId class - Milestone (uses standard numeric $id)
/// </summary>
public class Milestone
{
    public string Name { get; set; } = string.Empty;
    public DateTime DueDate { get; set; }
    public bool IsCompleted { get; set; }
}

/// <summary>
/// Non-IId class - TaskMetadata (uses standard numeric $id)
/// </summary>
public class TaskMetadata
{
    public DateTime CreatedAt { get; set; }
    public DateTime? ModifiedAt { get; set; }
    public string CreatedBy { get; set; } = string.Empty;
    public int Priority { get; set; }
    public string[] Tags { get; set; } = Array.Empty<string>();
}

#endregion

[TestClass]
public sealed class JsonExtensionTests
{
    private FruitBankSignalRClient _signalRClient = null!;

    [TestInitialize]
    public void TestInit()
    {
        if (!FruitBankConstClient.BaseUrl.Contains("localhost:")) throw new Exception("NEM LOCALHOST-ON TESZTELÜNK!");

        _signalRClient = TestSignalRClientFactory.Create(nameof(FruitBankClientTests));
    }

    [TestMethod]
    public async Task GetMeasuringUsersTest()
    {
        var users = await _signalRClient.GetMeasuringUsers();

        Assert.IsNotNull(users);
        Assert.IsTrue(users.Count != 0);
        Assert.IsTrue(users.All(x => !x.Email.IsNullOrEmpty() && !x.Deleted));
    }

    [TestMethod]
    public async Task RealOrderDto_JSON_Merge_Ref_Test()
    {
        var initialCount = 28;
        List<OrderDto>? pendingOrderDtos = (await _signalRClient.GetPendingOrderDtos())?.Take(initialCount).ToList();

        Assert.IsNotNull(pendingOrderDtos);
        Assert.IsTrue(pendingOrderDtos.Count != 0);

        var itemToDuplicate = pendingOrderDtos[0];
        var listWithDuplication = pendingOrderDtos.ToList();
        listWithDuplication.Add(itemToDuplicate);

        var settings = SerializeObjectExtensions.Options;
        var dtNow = DateTime.UtcNow;

        listWithDuplication[0].PaidDateUtc = dtNow;
        
        var itemWithGenericAttributes = listWithDuplication.FirstOrDefault(x => x.GenericAttributes?.Count > 0);
        Assert.IsNotNull(itemWithGenericAttributes, "Nincs olyan OrderDto a listában, amelynek lenne GenericAttributes eleme!");
        
        itemWithGenericAttributes.GenericAttributes[0].CreatedOrUpdatedDateUTC = dtNow;

        var json = JsonConvert.SerializeObject(listWithDuplication, settings);

        Assert.IsTrue(json.Contains("$id"), "JSON-nak tartalmaznia kell $id tokeneket");
        Assert.IsTrue(json.Contains("$ref"), "JSON-nak tartalmaznia kell $ref tokeneket");

        pendingOrderDtos.DeepPopulateWithMerge(json, settings);

        Assert.AreEqual(initialCount, pendingOrderDtos.Count);
        Assert.IsTrue(itemToDuplicate.PaidDateUtc == dtNow);
        Assert.IsTrue(itemWithGenericAttributes.GenericAttributes[0].CreatedOrUpdatedDateUTC == dtNow);
    }

    /// <summary>
    /// Comprehensive test for hybrid reference handling:
    /// - 5 levels deep hierarchy
    /// - IId&lt;int&gt;, IId&lt;Guid&gt;, IId&lt;long&gt; types (semantic IDs)
    /// - Non-IId types (numeric IDs)
    /// - Collections (List&lt;T&gt;), Arrays, and single object properties
    /// - Back-references to parent objects
    /// - Shared object references
    /// </summary>
    [TestMethod]
    public void HybridReferenceHandling_DeepHierarchy_Test()
    {
        // Arrange - Create test data with 5 levels of depth
        var sharedAddress = new Address
        {
            Street = "123 Shared Street",
            City = "Budapest",
            Country = "Hungary",
            PostalCode = "1111"
        };

        var company = new Company
        {
            Id = 1,
            Name = "Acme Corporation",
            HeadquartersAddress = sharedAddress
        };

        var department1 = new Department
        {
            Id = 101,
            Name = "Engineering",
            ParentCompany = company, // Back-reference to Level 1
            OfficeAddress = new Address
            {
                Street = "456 Tech Ave",
                City = "Budapest",
                Country = "Hungary",
                PostalCode = "2222"
            }
        };

        var department2 = new Department
        {
            Id = 102,
            Name = "Marketing",
            ParentCompany = company, // Back-reference to Level 1 (same company)
            OfficeAddress = sharedAddress // Shared address reference
        };

        company.Departments.Add(department1);
        company.Departments.Add(department2);

        var employee1 = new Employee
        {
            Id = 1001,
            FullName = "John Doe",
            Email = "john.doe@acme.com",
            Department = department1, // Back-reference to Level 2
            Contact = new ContactInfo
            {
                Phone = "+36-1-111-1111",
                Mobile = "+36-30-111-1111",
                HomeAddress = sharedAddress // Shared address reference
            },
            Skills = new List<Skill>
            {
                new() { Name = "C#", Level = 5 },
                new() { Name = "Azure", Level = 4 }
            }
        };

        var employee2 = new Employee
        {
            Id = 1002,
            FullName = "Jane Smith",
            Email = "jane.smith@acme.com",
            Department = department1, // Back-reference to same department
            Contact = new ContactInfo
            {
                Phone = "+36-1-222-2222",
                Mobile = "+36-30-222-2222",
                HomeAddress = new Address
                {
                    Street = "789 Home Lane",
                    City = "Debrecen",
                    Country = "Hungary",
                    PostalCode = "3333"
                }
            },
            Skills = new List<Skill>
            {
                new() { Name = "JavaScript", Level = 5 },
                new() { Name = "React", Level = 4 }
            }
        };

        var employee3 = new Employee
        {
            Id = 1003,
            FullName = "Bob Wilson",
            Email = "bob.wilson@acme.com",
            Department = department2, // Different department
            Skills = new List<Skill>
            {
                new() { Name = "Marketing", Level = 5 }
            }
        };

        department1.Employees.Add(employee1);
        department1.Employees.Add(employee2);
        department2.Employees.Add(employee3);

        // Board members array
        company.BoardMembers = new[] { employee1, employee3 }; // Shared employee references

        var project1 = new Project
        {
            Id = Guid.NewGuid(),
            ProjectName = "Project Alpha",
            StartDate = DateTime.UtcNow.AddMonths(-3),
            OwningDepartment = department1, // Back-reference to Level 2
            TeamMembers = new List<Employee> { employee1, employee2 }, // Shared employee references
            Milestones = new[]
            {
                new Milestone { Name = "Phase 1", DueDate = DateTime.UtcNow.AddMonths(-2), IsCompleted = true },
                new Milestone { Name = "Phase 2", DueDate = DateTime.UtcNow.AddMonths(-1), IsCompleted = true },
                new Milestone { Name = "Phase 3", DueDate = DateTime.UtcNow.AddMonths(1), IsCompleted = false }
            }
        };

        var project2 = new Project
        {
            Id = Guid.NewGuid(),
            ProjectName = "Project Beta",
            StartDate = DateTime.UtcNow.AddMonths(-1),
            OwningDepartment = department1, // Same department
            TeamMembers = new List<Employee> { employee2 }, // Shared employee reference
            Milestones = new[]
            {
                new Milestone { Name = "Initial", DueDate = DateTime.UtcNow.AddMonths(2), IsCompleted = false }
            }
        };

        department1.ActiveProjects = new[] { project1, project2 };

        employee1.AssignedProjects.Add(project1);
        employee2.AssignedProjects.Add(project1);
        employee2.AssignedProjects.Add(project2);

        var task1 = new ProjectTask
        {
            Id = 10001L,
            Title = "Implement Feature X",
            Description = "Detailed implementation of Feature X",
            IsCompleted = false,
            ParentProject = project1, // Back-reference to Level 4
            AssignedTo = employee1, // Reference to Level 3
            Metadata = new TaskMetadata
            {
                CreatedAt = DateTime.UtcNow.AddDays(-10),
                CreatedBy = "admin",
                Priority = 1,
                Tags = new[] { "urgent", "feature", "backend" }
            }
        };

        var task2 = new ProjectTask
        {
            Id = 10002L,
            Title = "Write Tests",
            Description = "Unit tests for Feature X",
            IsCompleted = false,
            ParentProject = project1, // Same project
            AssignedTo = employee2, // Different employee
            Metadata = new TaskMetadata
            {
                CreatedAt = DateTime.UtcNow.AddDays(-5),
                CreatedBy = "admin",
                Priority = 2,
                Tags = new[] { "testing", "quality" }
            }
        };

        var task3 = new ProjectTask
        {
            Id = 10003L,
            Title = "Review Code",
            Description = "Code review for Feature X",
            IsCompleted = false,
            ParentProject = project1,
            AssignedTo = employee1, // Same employee as task1 (circular reference)
            Metadata = new TaskMetadata
            {
                CreatedAt = DateTime.UtcNow.AddDays(-3),
                CreatedBy = "lead",
                Priority = 1,
                Tags = new[] { "review" }
            }
        };

        project1.Tasks.Add(task1);
        project1.Tasks.Add(task2);
        project1.Tasks.Add(task3);

        var settings = SerializeObjectExtensions.Options;

        // Act - Serialize
        var json = JsonConvert.SerializeObject(company, settings);

        // Assert - JSON structure
        Assert.IsNotNull(json);
        Assert.IsTrue(json.Length > 0, "JSON should not be empty");

        // Check for semantic IDs (IId<T> types)
        Assert.IsTrue(json.Contains("\"$id\":\"Company_1\""), "Should contain semantic ID for Company");
        Assert.IsTrue(json.Contains("\"$id\":\"Department_101\""), "Should contain semantic ID for Department 101");
        Assert.IsTrue(json.Contains("\"$id\":\"Department_102\""), "Should contain semantic ID for Department 102");
        Assert.IsTrue(json.Contains("\"$id\":\"Employee_1001\""), "Should contain semantic ID for Employee 1001");
        Assert.IsTrue(json.Contains("\"$id\":\"Employee_1002\""), "Should contain semantic ID for Employee 1002");
        Assert.IsTrue(json.Contains("\"$id\":\"Employee_1003\""), "Should contain semantic ID for Employee 1003");
        Assert.IsTrue(json.Contains("\"$id\":\"ProjectTask_10001\""), "Should contain semantic ID for ProjectTask 10001");

        // Check for $ref tokens (back-references)
        Assert.IsTrue(json.Contains("\"$ref\":\"Company_1\""), "Should contain $ref to Company");
        Assert.IsTrue(json.Contains("\"$ref\":\"Department_101\""), "Should contain $ref to Department");
        Assert.IsTrue(json.Contains("\"$ref\":\"Employee_1001\""), "Should contain $ref to Employee 1001");
        Assert.IsTrue(json.Contains("\"$ref\":\"Employee_1002\""), "Should contain $ref to Employee 1002");

        // Check for numeric IDs (non-IId types like Address, ContactInfo, etc.)
        // These should have simple numeric $id values
        Assert.IsTrue(System.Text.RegularExpressions.Regex.IsMatch(json, @"\$id"":""[0-9]+"""), 
            "Should contain numeric $id for non-IId types");

        // Act - Deserialize
        var deserializedCompany = JsonConvert.DeserializeObject<Company>(json, settings);

        // Assert - Deserialized structure
        Assert.IsNotNull(deserializedCompany);
        Assert.AreEqual(1, deserializedCompany.Id);
        Assert.AreEqual("Acme Corporation", deserializedCompany.Name);

        // Verify departments
        Assert.AreEqual(2, deserializedCompany.Departments.Count);
        var deserializedDept1 = deserializedCompany.Departments.First(d => d.Id == 101);
        var deserializedDept2 = deserializedCompany.Departments.First(d => d.Id == 102);

        // Verify back-references to company
        Assert.AreSame(deserializedCompany, deserializedDept1.ParentCompany, 
            "Department1's ParentCompany should be the same instance as deserializedCompany");
        Assert.AreSame(deserializedCompany, deserializedDept2.ParentCompany, 
            "Department2's ParentCompany should be the same instance as deserializedCompany");

        // Verify employees
        Assert.AreEqual(2, deserializedDept1.Employees.Count);
        var deserializedEmp1 = deserializedDept1.Employees.First(e => e.Id == 1001);
        var deserializedEmp2 = deserializedDept1.Employees.First(e => e.Id == 1002);

        // Verify employee back-references to department
        Assert.AreSame(deserializedDept1, deserializedEmp1.Department,
            "Employee1's Department should be the same instance as deserializedDept1");
        Assert.AreSame(deserializedDept1, deserializedEmp2.Department,
            "Employee2's Department should be the same instance as deserializedDept1");

        // Verify board members are same instances as department employees
        Assert.AreEqual(2, deserializedCompany.BoardMembers.Length);
        Assert.AreSame(deserializedEmp1, deserializedCompany.BoardMembers.First(e => e.Id == 1001),
            "BoardMember should be the same instance as deserializedEmp1");

        // Verify projects
        Assert.AreEqual(2, deserializedDept1.ActiveProjects.Length);
        var deserializedProject1 = deserializedDept1.ActiveProjects.First(p => p.ProjectName == "Project Alpha");

        // Verify project back-reference to department
        Assert.AreSame(deserializedDept1, deserializedProject1.OwningDepartment,
            "Project1's OwningDepartment should be the same instance as deserializedDept1");

        // Verify project team members are same instances
        Assert.IsTrue(deserializedProject1.TeamMembers.Any(e => ReferenceEquals(e, deserializedEmp1)),
            "Project1's TeamMembers should contain the same instance as deserializedEmp1");
        Assert.IsTrue(deserializedProject1.TeamMembers.Any(e => ReferenceEquals(e, deserializedEmp2)),
            "Project1's TeamMembers should contain the same instance as deserializedEmp2");

        // Verify tasks
        Assert.AreEqual(3, deserializedProject1.Tasks.Count);
        var deserializedTask1 = deserializedProject1.Tasks.First(t => t.Id == 10001L);

        // Verify task back-references
        Assert.AreSame(deserializedProject1, deserializedTask1.ParentProject,
            "Task1's ParentProject should be the same instance as deserializedProject1");
        Assert.AreSame(deserializedEmp1, deserializedTask1.AssignedTo,
            "Task1's AssignedTo should be the same instance as deserializedEmp1");

        // Verify shared Address instances (non-IId type)
        Assert.AreSame(deserializedCompany.HeadquartersAddress, deserializedDept2.OfficeAddress,
            "HeadquartersAddress and Dept2's OfficeAddress should be the same instance (shared Address)");
        Assert.AreSame(deserializedCompany.HeadquartersAddress, deserializedEmp1.Contact?.HomeAddress,
            "HeadquartersAddress and Emp1's HomeAddress should be the same instance (shared Address)");

        // Verify milestones (non-IId array)
        Assert.AreEqual(3, deserializedProject1.Milestones.Length);
        Assert.IsTrue(deserializedProject1.Milestones.Any(m => m.Name == "Phase 1" && m.IsCompleted));

        // Verify skills (non-IId collection)
        Assert.AreEqual(2, deserializedEmp1.Skills.Count);
        Assert.IsTrue(deserializedEmp1.Skills.Any(s => s.Name == "C#" && s.Level == 5));

        // Verify task metadata (non-IId object)
        Assert.IsNotNull(deserializedTask1.Metadata);
        Assert.AreEqual("admin", deserializedTask1.Metadata.CreatedBy);
        Assert.AreEqual(1, deserializedTask1.Metadata.Priority);
        Assert.AreEqual(3, deserializedTask1.Metadata.Tags.Length);
    }

    /// <summary>
    /// Test for DeepPopulateWithMerge with hybrid references
    /// </summary>
    [TestMethod]
    public void HybridReferenceHandling_DeepPopulateWithMerge_Test()
    {
        // Arrange - Create initial data
        var company = new Company
        {
            Id = 1,
            Name = "Original Company Name",
            HeadquartersAddress = new Address { City = "Original City" }
        };

        var department = new Department
        {
            Id = 101,
            Name = "Original Department",
            ParentCompany = company
        };
        company.Departments.Add(department);

        var employee = new Employee
        {
            Id = 1001,
            FullName = "Original Name",
            Email = "original@email.com",
            Department = department
        };
        department.Employees.Add(employee);

        // Create modified version
        var modifiedCompany = new Company
        {
            Id = 1,
            Name = "Modified Company Name",
            HeadquartersAddress = new Address { City = "Modified City" }
        };

        var modifiedDepartment = new Department
        {
            Id = 101,
            Name = "Modified Department",
            ParentCompany = modifiedCompany
        };
        modifiedCompany.Departments.Add(modifiedDepartment);

        var modifiedEmployee = new Employee
        {
            Id = 1001,
            FullName = "Modified Name",
            Email = "modified@email.com",
            Department = modifiedDepartment
        };
        modifiedDepartment.Employees.Add(modifiedEmployee);

        // Add a new employee in the modified version
        var newEmployee = new Employee
        {
            Id = 1002,
            FullName = "New Employee",
            Email = "new@email.com",
            Department = modifiedDepartment
        };
        modifiedDepartment.Employees.Add(newEmployee);

        var settings = SerializeObjectExtensions.Options;
        var modifiedJson = JsonConvert.SerializeObject(modifiedCompany, settings);

        // Store original references
        var originalCompanyRef = company;
        var originalDepartmentRef = department;
        var originalEmployeeRef = employee;

        // Act - Deep populate with merge
        company.DeepPopulateWithMerge(modifiedJson, settings);

        // Assert - Same instances should be updated, not replaced
        Assert.AreSame(originalCompanyRef, company, "Company instance should be the same");
        Assert.AreEqual("Modified Company Name", company.Name, "Company name should be updated");
        Assert.AreEqual("Modified City", company.HeadquartersAddress.City, "Address should be updated");

        Assert.AreEqual(1, company.Departments.Count, "Should still have 1 department");
        Assert.AreSame(originalDepartmentRef, company.Departments[0], "Department instance should be the same");
        Assert.AreEqual("Modified Department", company.Departments[0].Name, "Department name should be updated");

        // The original employee should be updated
        var updatedEmployee = company.Departments[0].Employees.FirstOrDefault(e => e.Id == 1001);
        Assert.IsNotNull(updatedEmployee);
        Assert.AreSame(originalEmployeeRef, updatedEmployee, "Original employee instance should be the same");
        Assert.AreEqual("Modified Name", updatedEmployee.FullName, "Employee name should be updated");
        Assert.AreEqual("modified@email.com", updatedEmployee.Email, "Employee email should be updated");

        // New employee should be added
        Assert.AreEqual(2, company.Departments[0].Employees.Count, "Should have 2 employees after merge");
        var addedEmployee = company.Departments[0].Employees.FirstOrDefault(e => e.Id == 1002);
        Assert.IsNotNull(addedEmployee, "New employee should be added");
        Assert.AreEqual("New Employee", addedEmployee.FullName);
    }

    /// <summary>
    /// Test that verifies circular references don't cause infinite loops
    /// </summary>
    [TestMethod]
    public void HybridReferenceHandling_CircularReferences_NoInfiniteLoop_Test()
    {
        // Arrange - Create circular reference structure
        var company = new Company { Id = 1, Name = "Test Company" };
        var department = new Department { Id = 101, Name = "Test Dept", ParentCompany = company };
        company.Departments.Add(department);
        
        var employee = new Employee { Id = 1001, FullName = "Test Employee", Department = department };
        department.Employees.Add(employee);
        
        var project = new Project 
        { 
            Id = Guid.NewGuid(), 
            ProjectName = "Test Project",
            OwningDepartment = department,
            TeamMembers = new List<Employee> { employee }
        };
        department.ActiveProjects = new[] { project };
        employee.AssignedProjects.Add(project);
        
        var task = new ProjectTask
        {
            Id = 10001L,
            Title = "Test Task",
            ParentProject = project,
            AssignedTo = employee // Circular: Task -> Employee -> Project -> Task's Project
        };
        project.Tasks.Add(task);

        var settings = SerializeObjectExtensions.Options;

        // Act - Should not throw StackOverflowException or timeout
        var json = JsonConvert.SerializeObject(company, settings);
        var deserialized = JsonConvert.DeserializeObject<Company>(json, settings);

        // Assert
        Assert.IsNotNull(deserialized);
        Assert.AreEqual(1, deserialized.Id);
        
        var deserializedDept = deserialized.Departments[0];
        var deserializedEmployee = deserializedDept.Employees[0];
        var deserializedProject = deserializedDept.ActiveProjects[0];
        var deserializedTask = deserializedProject.Tasks[0];

        // Verify circular references are correctly resolved
        Assert.AreSame(deserialized, deserializedDept.ParentCompany);
        Assert.AreSame(deserializedDept, deserializedEmployee.Department);
        Assert.AreSame(deserializedDept, deserializedProject.OwningDepartment);
        Assert.AreSame(deserializedProject, deserializedTask.ParentProject);
        Assert.AreSame(deserializedEmployee, deserializedTask.AssignedTo);
        Assert.AreSame(deserializedEmployee, deserializedProject.TeamMembers[0]);
        Assert.AreSame(deserializedProject, deserializedEmployee.AssignedProjects[0]);
    }
}