using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Enums;
using System.Collections.Generic;

namespace CMSMicroservice.Infrastructure.Services;

/// <summary>
/// پیاده‌سازی سرویس محاسبه موقعیت در Binary Tree
/// </summary>
public class NetworkPlacementService : INetworkPlacementService
{
    private readonly IApplicationDbContext _context;
    private readonly ILogger<NetworkPlacementService> _logger;

    public NetworkPlacementService(
        IApplicationDbContext context,
        ILogger<NetworkPlacementService> logger)
    {
        _context = context;
        _logger = logger;
    }

    public async Task<NetworkLeg?> CalculateLegPositionAsync(long parentId, CancellationToken cancellationToken = default)
    {
        // بررسی وجود Parent
        var parentExists = await _context.Users.AnyAsync(u => u.Id == parentId, cancellationToken);
        if (!parentExists)
        {
            _logger.LogWarning("Parent {ParentId} does not exist", parentId);
            return null;
        }

        // شمارش فرزندان فعلی
        var children = await _context.Users
            .Where(u => u.NetworkParentId == parentId)
            .Select(u => new { u.LegPosition })
            .ToListAsync(cancellationToken);

        if (children.Count >= 2)
        {
            _logger.LogWarning("Parent {ParentId} already has 2 children. Binary Tree is full!", parentId);
            return null; // Binary Tree پر است
        }

        // بررسی کدام Leg خالی است
        var hasLeft = children.Any(c => c.LegPosition == NetworkLeg.Left);
        var hasRight = children.Any(c => c.LegPosition == NetworkLeg.Right);

        if (!hasLeft)
        {
            _logger.LogDebug("Parent {ParentId}: Left leg is available", parentId);
            return NetworkLeg.Left;
        }

        if (!hasRight)
        {
            _logger.LogDebug("Parent {ParentId}: Right leg is available", parentId);
            return NetworkLeg.Right;
        }

        // نباید به اینجا برسیم (چون Count < 2 بود)
        _logger.LogError("Unexpected state: Parent {ParentId} has {Count} children but no available leg", 
            parentId, children.Count);
        return null;
    }

    public async Task<bool> CanAcceptChildAsync(long parentId, CancellationToken cancellationToken = default)
    {
        var childCount = await _context.Users
            .CountAsync(u => u.NetworkParentId == parentId, cancellationToken);

        return childCount < 2;
    }

    public async Task<long?> FindAvailableParentAsync(long rootParentId, CancellationToken cancellationToken = default)
    {
        // BFS (Breadth-First Search) برای پیدا کردن اولین Parent با جای خالی
        var queue = new Queue<long>();
        queue.Enqueue(rootParentId);
        var visited = new HashSet<long>();

        while (queue.Count > 0)
        {
            var currentParentId = queue.Dequeue();

            if (visited.Contains(currentParentId))
                continue;

            visited.Add(currentParentId);

            // بررسی کنید که آیا این Parent می‌تواند فرزند بپذیرد
            var canAccept = await CanAcceptChildAsync(currentParentId, cancellationToken);
            if (canAccept)
            {
                _logger.LogInformation("Found available parent: {ParentId}", currentParentId);
                return currentParentId;
            }

            // اضافه کردن فرزندان به صف برای جستجو
            var children = await _context.Users
                .Where(u => u.NetworkParentId == currentParentId)
                .Select(u => u.Id)
                .ToListAsync(cancellationToken);

            foreach (var childId in children)
            {
                queue.Enqueue(childId);
            }
        }

        _logger.LogWarning("No available parent found in network starting from {RootParentId}", rootParentId);
        return null; // هیچ Parent خالی پیدا نشد
    }
}
