using CMSMicroservice.Protobuf.Protos.NetworkMembership;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.NetworkMembershipCQ.Commands.JoinNetwork;
using CMSMicroservice.Application.NetworkMembershipCQ.Commands.MoveInNetwork;
using CMSMicroservice.Application.NetworkMembershipCQ.Commands.RemoveFromNetwork;
using CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetUserNetworkPosition;
using CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkTree;
using CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkMembershipHistory;
using CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkStatistics;
using CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetMyNetworkTree;
using CMSMicroservice.Application.Common.Interfaces;
using Mapster;
using CMSMicroservice.Domain.Enums;

namespace CMSMicroservice.WebApi.Services;

public class NetworkMembershipService : NetworkMembershipContract.NetworkMembershipContractBase
{
    private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
    private readonly ISender _sender;
    private readonly ICurrentUserService _currentUserService;

    public NetworkMembershipService(
        IDispatchRequestToCQRS dispatchRequestToCQRS,
        ISender sender,
        ICurrentUserService currentUserService)
    {
        _dispatchRequestToCQRS = dispatchRequestToCQRS;
        _sender = sender;
        _currentUserService = currentUserService;
    }

    public override async Task<Empty> JoinNetwork(JoinNetworkRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<JoinNetworkRequest, JoinNetworkCommand>(request, context);
    }

    public override async Task<Empty> ChangeNetworkParent(ChangeNetworkParentRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<ChangeNetworkParentRequest, MoveInNetworkCommand>(request, context);
    }

    public override async Task<Empty> RemoveFromNetwork(RemoveFromNetworkRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<RemoveFromNetworkRequest, RemoveFromNetworkCommand>(request, context);
    }

    public override async Task<GetUserNetworkResponse> GetUserNetwork(GetUserNetworkRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetUserNetworkRequest, GetUserNetworkPositionQuery, GetUserNetworkResponse>(request, context);
    }

    public override async Task<GetNetworkTreeResponse> GetNetworkTree(GetNetworkTreeRequest request, ServerCallContext context)
    {

        return await _dispatchRequestToCQRS.Handle<GetNetworkTreeRequest, GetNetworkTreeQuery, GetNetworkTreeResponse>(request, context);
    }

    public override async Task<GetNetworkMembershipHistoryResponse> GetNetworkMembershipHistory(GetNetworkMembershipHistoryRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetNetworkMembershipHistoryRequest, GetNetworkMembershipHistoryQuery, GetNetworkMembershipHistoryResponse>(request, context);
    }

    public override async Task<GetNetworkStatisticsResponse> GetNetworkStatistics(GetNetworkStatisticsRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetNetworkStatisticsRequest, GetNetworkStatisticsQuery, GetNetworkStatisticsResponse>(request, context);
    }

    // ============= Customer-specific Methods =============

    public override async Task<GetMyNetworkTreeResponse> GetMyNetworkTree(GetMyNetworkTreeRequest request, ServerCallContext context)
    {
        // Use Customer-specific Query that gets UserId from ICurrentUserService
        var query = new GetMyNetworkTreeQuery
        {
            MaxDepth = request.MaxDepth > 0 ? request.MaxDepth : 3
        };

        var tree = await _sender.Send(query, context.CancellationToken);

        if (tree == null)
        {
            return new GetMyNetworkTreeResponse
            {
                RootNode = null,
                TotalMembers = 0,
                CurrentDepth = 0
            };
        }

        // Convert NetworkTreeDto to NetworkTreeNodeModel
        var rootNode = ConvertToNodeModel(tree);

        return new GetMyNetworkTreeResponse
        {
            RootNode = rootNode,
            TotalMembers = CountNodes(tree),
            CurrentDepth = tree.CurrentDepth
        };
    }

    public override async Task<GetMyNetworkTreeResponse> GetSubordinateTree(GetSubordinateTreeRequest request, ServerCallContext context)
    {
        // Get tree for a specific subordinate user
        var query = new GetNetworkTreeQuery
        {
            UserId = request.TargetUserId,
            MaxDepth = request.MaxDepth > 0 ? request.MaxDepth : 3
        };

        var tree = await _sender.Send(query, context.CancellationToken);

        if (tree == null)
        {
            return new GetMyNetworkTreeResponse
            {
                RootNode = null,
                TotalMembers = 0,
                CurrentDepth = 0
            };
        }

        var rootNode = ConvertToNodeModel(tree);

        return new GetMyNetworkTreeResponse
        {
            RootNode = rootNode,
            TotalMembers = CountNodes(tree),
            CurrentDepth = tree.CurrentDepth
        };
    }

    public override async Task<GetMyNetworkStatisticsResponse> GetMyNetworkStatistics(Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context)
    {
        // Customer endpoint: resolve userId from JWT
        if (!long.TryParse(_currentUserService.UserId, out var userId) || userId <= 0)
            throw new RpcException(new Status(StatusCode.Unauthenticated, "کاربر احراز هویت نشده است"));

        var query = new GetNetworkStatisticsQuery { UserId = userId };
        var stats = await _sender.Send(query, context.CancellationToken);

        return stats.Adapt<GetMyNetworkStatisticsResponse>();
    }

    // Helper methods for tree conversion
    private NetworkTreeNodeModel ConvertToNodeModel(NetworkTreeDto dto)
    {
        var node = new NetworkTreeNodeModel
        {
            UserId = dto.UserId,
            UserName = $"{dto.FirstName} {dto.LastName}".Trim(),
            FullName = $"{dto.FirstName} {dto.LastName}".Trim(),
            NetworkLeg = dto.LegPosition.HasValue ? (int)dto.LegPosition.Value : 0,
            NetworkLevel = dto.CurrentDepth,
            Level = dto.CurrentDepth,
            IsActive = dto.IsClubActive,
            IsClubActive = dto.IsClubActive,
            ReferralCode = dto.ReferralCode ?? "",
            Mobile = dto.Mobile ?? "",
            Position = dto.LegPosition.HasValue ? dto.LegPosition.Value.ToString() : "Root"
        };

        if (dto.ClubActivatedAt.HasValue)
        {
            node.ClubActivatedAt = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(
                DateTime.SpecifyKind(dto.ClubActivatedAt.Value, DateTimeKind.Utc));
        }

        if (dto.UserCreated != null)
        {
            node.UserCreated = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(
                dto.UserCreated.UtcDateTime);
        }

        if (dto.ActivationWeekDefinitionId.HasValue)
        {
            node.ActivationWeekDefinitionId = dto.ActivationWeekDefinitionId.Value;
        }

        node.IsActivatedInTargetWeek = dto.IsActivatedInTargetWeek;

        node.PackageName = dto.PackageName ?? string.Empty;
        if (dto.PackageId.HasValue)
        {
            node.PackageId = dto.PackageId.Value;
        }

        node.GoldLeftLegTotal = dto.GoldLeftLegTotal;
        node.GoldRightLegTotal = dto.GoldRightLegTotal;
        node.SilverLeftLegTotal = dto.SilverLeftLegTotal;
        node.SilverRightLegTotal = dto.SilverRightLegTotal;

        if (dto.IsNewActivation.HasValue)
        {
            node.IsNewActivation = dto.IsNewActivation.Value;
        }

        // Recursively convert children
        if (dto.LeftChild != null)
        {
            node.LeftChild = ConvertToNodeModel(dto.LeftChild);
        }

        if (dto.RightChild != null)
        {
            node.RightChild = ConvertToNodeModel(dto.RightChild);
        }

        return node;
    }

    private int CountNodes(NetworkTreeDto tree)
    {
        if (tree == null) return 0;
        return 1 + CountNodes(tree.LeftChild) + CountNodes(tree.RightChild);
    }
}
