using CMSMicroservice.Application.Common.Authorization;
using CMSMicroservice.Protobuf.Protos.UserOrder;
using CMSMicroservice.Application.UserOrderCQ.Queries.GetCustomerOrders;
using CMSMicroservice.Application.UserOrderCQ.Queries.GetCustomerOrder;
using CMSMicroservice.Application.UserOrderCQ.Queries.GetCustomerOrderHistory;
using CMSMicroservice.Application.OrderManagementCQ.Commands.UpdateOrderStatus;
using CMSMicroservice.Application.OrderManagementCQ.Commands.CancelOrderByAdmin;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Common;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Entities.Club;
using CMSMicroservice.Domain.Entities.Order;
using CMSMicroservice.Domain.Enums;
using AppModels = CMSMicroservice.Application.Common.Models;
using Grpc.Core;
using Google.Protobuf.WellKnownTypes;
using System.Collections.Generic;
using System.Linq;
using CMSMicroservice.Protobuf.Protos;
using MediatR;
using Mapster;
using Microsoft.EntityFrameworkCore;

namespace CMSMicroservice.WebApi.Services;

public class UserOrderService : UserOrderContract.UserOrderContractBase
{
    private readonly ISender _sender;
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUserService;
    private readonly IInventoryService _inventoryService;

    public UserOrderService(ISender sender, IApplicationDbContext context, ICurrentUserService currentUserService, IInventoryService inventoryService)
    {
        _sender = sender;
        _context = context;
        _currentUserService = currentUserService;
        _inventoryService = inventoryService;
    }
    [RequiresPermission(PermissionNames.OrdersCreate)]
    public override async Task<CreateNewUserOrderResponse> CreateNewUserOrder(CreateNewUserOrderRequest request, ServerCallContext context)
    {
        var order = new UserOrder
        {
            Amount = request.Amount,
            PackageId = request.PackageId > 0 ? request.PackageId : null,
            TransactionId = request.TransactionId,
            PaymentStatus = request.HasPaymentStatus
                ? (Domain.Enums.PaymentStatus)(int)request.PaymentStatus
                : Domain.Enums.PaymentStatus.Pending,
            PaymentDate = request.PaymentDate?.ToDateTime(),
            UserId = request.UserId,
            UserAddressId = request.UserAddressId,
            PaymentMethod = request.HasPaymentMethod
                ? (Domain.Enums.PaymentMethod)(int)request.PaymentMethod
                : null,
            DeliveryStatus = Domain.Enums.DeliveryStatus.Pending
        };
        
        _context.UserOrders.Add(order);
        await _context.SaveChangesAsync(context.CancellationToken);
        
        return new CreateNewUserOrderResponse { Id = order.Id };
    }
    
    [RequiresPermission(PermissionNames.OrdersUpdate)]
    public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateUserOrder(UpdateUserOrderRequest request, ServerCallContext context)
    {
        var order = await _context.UserOrders.FindAsync(new object[] { request.Id }, context.CancellationToken);
        if (order == null)
            throw new RpcException(new Status(StatusCode.NotFound, "سفارش یافت نشد"));
        
        if (request.Amount != null) order.Amount = request.Amount.Value;
        if (request.PackageId != null) order.PackageId = request.PackageId.Value;
        if (request.TransactionId != null) order.TransactionId = request.TransactionId.Value;
        if (request.HasPaymentStatus) order.PaymentStatus = (Domain.Enums.PaymentStatus)(int)request.PaymentStatus;
        if (request.PaymentDate != null) order.PaymentDate = request.PaymentDate.ToDateTime();
        if (request.UserId != null) order.UserId = request.UserId.Value;
        if (request.UserAddressId != null) order.UserAddressId = request.UserAddressId.Value;
        if (request.HasPaymentMethod) order.PaymentMethod = (Domain.Enums.PaymentMethod)(int)request.PaymentMethod;
        if (request.HasDeliveryStatus) order.DeliveryStatus = (Domain.Enums.DeliveryStatus)(int)request.DeliveryStatus;
        if (request.TrackingCode != null) order.TrackingCode = request.TrackingCode;
        if (request.DeliveryDescription != null) order.DeliveryDescription = request.DeliveryDescription;
        
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Google.Protobuf.WellKnownTypes.Empty();
    }
    
    [RequiresPermission(PermissionNames.OrdersDelete)]
    public override async Task<Google.Protobuf.WellKnownTypes.Empty> DeleteUserOrder(DeleteUserOrderRequest request, ServerCallContext context)
    {
        var order = await _context.UserOrders.FindAsync(new object[] { request.Id }, context.CancellationToken);
        if (order == null)
            throw new RpcException(new Status(StatusCode.NotFound, "سفارش یافت نشد"));
        
        order.IsDeleted = true;
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Google.Protobuf.WellKnownTypes.Empty();
    }
    
    [RequiresPermission(PermissionNames.OrdersView)]
    public override async Task<GetUserOrderResponse> GetUserOrder(GetUserOrderRequest request, ServerCallContext context)
    {
        var query = new GetCustomerOrderQuery
        {
            OrderId = request.Id,
            UserId = 0 // Admin: no user filter, can view any order by ID
        };

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

        var response = new GetUserOrderResponse
        {
            Id = result.Id,
            Amount = result.Amount,
            PackageId = result.PackageId ?? 0,
            TransactionId = result.TransactionId,
            UserId = result.UserId,
            UserAddressId = result.UserAddressId,
            UserAddressText = result.UserAddressText,
            TrackingCode = result.TrackingCode,
            DeliveryDescription = result.DeliveryDescription,
            UserFullName = result.UserFullName,
            UserNationalCode = result.UserNationalCode,
            PostalCode = result.PostalCode,
            UserMobile = result.UserMobile
        };

        // VAT Info
        if (result.VatAmount > 0)
        {
            response.VatInfo = new OrderVATInfo
            {
                VatRate = result.VatPercentage / 100,
                BaseAmount = result.Amount - result.VatAmount,
                VatAmount = result.VatAmount,
                TotalAmount = result.Amount,
                IsPaid = result.PaymentStatus == Domain.Enums.PaymentStatus.Success
            };
        }

        response.PaymentStatus = (CMSMicroservice.Protobuf.Protos.PaymentStatus)result.PaymentStatus;
        if (result.PaymentDate.HasValue)
            response.PaymentDate = Timestamp.FromDateTime(DateTime.SpecifyKind(result.PaymentDate.Value, DateTimeKind.Utc));

        if (result.PaymentMethod.HasValue)
            response.PaymentMethod = (CMSMicroservice.Protobuf.Protos.PaymentMethod)result.PaymentMethod.Value;

        response.DeliveryStatus = (CMSMicroservice.Protobuf.Protos.DeliveryStatus)result.DeliveryStatus;

        foreach (var fd in result.FactorDetails)
        {
            response.FactorDetails.Add(new GetUserOrderResponseFactorDetail
            {
                ProductId = fd.ProductId,
                ProductTitle = fd.ProductTitle,
                ProductThumbnailPath = fd.ProductThumbnailPath,
                UnitPrice = fd.UnitPrice,
                Count = fd.Count,
                UnitDiscountPrice = fd.UnitDiscountPrice
            });
        }

        return response;
    }
    
    [RequiresPermission(PermissionNames.OrdersView)]
    public override async Task<GetAllUserOrderByFilterResponse> GetAllUserOrderByFilter(GetAllUserOrderByFilterRequest request, ServerCallContext context)
    {
        // Admin API - can view all orders or filter by specific user
        var query = new GetCustomerOrdersQuery
        {
            UserId = request.Filter?.UserId ?? 0, // 0 means all users (admin view)
            PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
            PaymentStatusFilter = request.Filter?.HasPaymentStatus == true
                ? (int?)request.Filter.PaymentStatus
                : null,
            DeliveryStatusFilter = request.Filter?.HasDeliveryStatus == true
                ? (int?)request.Filter.DeliveryStatus
                : null,
            FromDate = request.Filter?.FromDate?.ToDateTime() ?? request.Filter?.PaymentDate?.ToDateTime(),
            ToDate = request.Filter?.ToDate?.ToDateTime()
        };

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

        var response = new GetAllUserOrderByFilterResponse
        {
            MetaData = result.MetaData.Adapt<MetaData>()
        };

        foreach (var model in result.Models)
        {
            var orderModel = new GetAllUserOrderByFilterResponseModel
            {
                Id = model.Id,
                Amount = model.Amount,
                PackageId = model.PackageId ?? 0,
                TransactionId = model.TransactionId,
                UserId = model.UserId,
                UserAddressId = model.UserAddressId,
                UserAddressText = model.UserAddressText,
                TrackingCode = model.TrackingCode,
                DeliveryDescription = model.DeliveryDescription,
                UserFullName = model.UserFullName,
                UserNationalCode = model.UserNationalCode,
                VatAmount = model.VatAmount,
                VatPercentage = model.VatPercentage
            };

            orderModel.PaymentStatus = (CMSMicroservice.Protobuf.Protos.PaymentStatus)model.PaymentStatus;
            if (model.PaymentDate.HasValue)
                orderModel.PaymentDate = Timestamp.FromDateTime(DateTime.SpecifyKind(model.PaymentDate.Value, DateTimeKind.Utc));
            
            if (model.PaymentMethod.HasValue)
                orderModel.PaymentMethod = (CMSMicroservice.Protobuf.Protos.PaymentMethod)model.PaymentMethod.Value;

            orderModel.DeliveryStatus = (CMSMicroservice.Protobuf.Protos.DeliveryStatus)model.DeliveryStatus;

            foreach (var fd in model.FactorDetails)
            {
                orderModel.FactorDetails.Add(new GetAllUserOrderByFilterResponseModelFactorDetail
                {
                    ProductId = fd.ProductId,
                    ProductTitle = fd.ProductTitle,
                    ProductThumbnailPath = fd.ProductThumbnailPath,
                    UnitPrice = fd.UnitPrice,
                    Count = fd.Count,
                    UnitDiscountPrice = fd.UnitDiscountPrice
                });
            }

            response.Models.Add(orderModel);
        }

        return response;
    }
    
    public override async Task<SubmitShopBuyOrderResponse> SubmitShopBuyOrder(SubmitShopBuyOrderRequest request, ServerCallContext context)
    {
        // Get UserId from JWT or request
        var userId = !string.IsNullOrEmpty(_currentUserService.UserId) 
            ? long.Parse(_currentUserService.UserId) 
            : request.UserId;
            
        if (userId == 0)
        {
            throw new RpcException(new Status(StatusCode.Unauthenticated, "User not authenticated"));
        }
        
        // Get user's cart items with product details
        var cartItems = await _context.UserCarts
            .Include(c => c.Product)
            .Where(c => c.UserId == userId && !c.IsDeleted)
            .ToListAsync(context.CancellationToken);
            
        if (!cartItems.Any())
        {
            throw new RpcException(new Status(StatusCode.FailedPrecondition, "سبد خرید خالی است"));
        }
        
        // Get user's default address
        var defaultAddress = await _context.UserAddresses
            .Where(a => a.UserId == userId && a.IsDefault && !a.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
            
        if (defaultAddress == null)
        {
            // Check if user has any address at all
            var hasAnyAddress = await _context.UserAddresses
                .AnyAsync(a => a.UserId == userId && !a.IsDeleted, context.CancellationToken);
                
            throw new RpcException(new Status(StatusCode.FailedPrecondition, 
                hasAnyAddress 
                    ? "لطفاً یک آدرس را به عنوان پیش‌فرض انتخاب کنید." 
                    : "آدرسی ثبت نشده است. لطفاً ابتدا یک آدرس اضافه کنید."));
        }
        
        // Calculate amounts
        const decimal vatRate = 0.10m;
        long baseAmount = cartItems.Sum(c => c.Product.Price * c.Count);
        long vatAmount = (long)(baseAmount * vatRate);
        long totalAmount = baseAmount + vatAmount;
        
        // Validate total amount
        if (request.TotalAmount > 0 && Math.Abs(totalAmount - request.TotalAmount) > 100)
        {
            throw new RpcException(new Status(StatusCode.InvalidArgument, 
                $"مبلغ نامعتبر است. محاسبه شده: {totalAmount}, دریافتی: {request.TotalAmount}"));
        }

        // Validate stock availability for all cart items before any financial operation
        foreach (var cartItem in cartItems)
        {
            var available = await _inventoryService.GetAvailableQuantityAsync(
                cartItem.ProductId, ProductType.RegularProduct, ct: context.CancellationToken);
            if (available < cartItem.Count)
            {
                throw new RpcException(new Status(StatusCode.FailedPrecondition,
                    $"موجودی «{cartItem.Product.Title}» کافی نیست. موجود: {available}، درخواستی: {cartItem.Count}"));
            }
        }
        
        // Get user's wallet
        var wallet = await _context.UserWallets
            .Where(w => w.UserId == userId)
            .FirstOrDefaultAsync(context.CancellationToken);
            
        if (wallet == null)
        {
            throw new RpcException(new Status(StatusCode.FailedPrecondition, "کیف پول یافت نشد"));
        }
        
        // Check wallet balance
        if (wallet.Balance < totalAmount)
        {
            throw new RpcException(new Status(StatusCode.FailedPrecondition, 
                $"موجودی کیف پول کافی نیست. موجودی: {wallet.Balance:N0} تومان، مورد نیاز: {totalAmount:N0} تومان"));
        }
        
        // Create transaction
        var transaction = new Transaction
        {
            Amount = totalAmount,
            Description = $"خرید محصولات - سفارش شماره در حال ایجاد",
            PaymentStatus = CMSMicroservice.Domain.Enums.PaymentStatus.Success,
            PaymentDate = DateTime.UtcNow,
            Type = CMSMicroservice.Domain.Enums.TransactionType.Buy,
            RefId = $"SHOP_{DateTime.UtcNow.Ticks}"
        };
        
        _context.Transactions.Add(transaction);
        await _context.SaveChangesAsync(context.CancellationToken);
        
        // Deduct from wallet
        var oldBalance = wallet.Balance;
        wallet.Balance -= totalAmount;
        
        // Create wallet change log
        var walletLog = new UserWalletHistory
        {
            WalletId = wallet.Id,
            CurrentBalance = wallet.Balance,
            ChangeValue = -totalAmount,
            CurrentNetworkBalance = wallet.NetworkBalance,
            ChangeNerworkValue = 0,
            CurrentDiscountBalance = wallet.DiscountBalance,
            ChangeDiscountValue = 0,
            IsIncrease = false,
            RefrenceId = transaction.Id
        };
        
        _context.UserWalletHistories.Add(walletLog);

        // ═══ Magic Wallet: Entry / Exit Trigger ═══
        // Q24: آستانه <= 100,000 تومان به جای == 0 (چون قیمت محصولات متفاوت است)
        if (wallet.Balance <= SystemConstants.MagicWalletEntryThreshold)
        {
            var user = await _context.Users
                .Include(u => u.ClubMembership)
                .FirstOrDefaultAsync(u => u.Id == userId, context.CancellationToken);

            if (wallet.WalletMode == WalletMode.Magic)
            {
                // بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی
                var userCycle = await _context.ClubMembershipCycles
                    .FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle, context.CancellationToken);
                var userPackage = userCycle != null
                    ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == userCycle.PackageId, context.CancellationToken)
                    : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, context.CancellationToken);
                var magicMaxDeposit = userPackage?.MagicWalletMaxDeposit
                    ?? throw new InvalidOperationException($"Package not found for magic wallet exit check, UserId={userId}");

                if (wallet.MagicTotalDeposited >= magicMaxDeposit)
                {
                // ── EXIT Magic Mode ──
                // هر دو شرط: Balance<=Threshold و سقف MagicMaxDeposit پر شده
                wallet.WalletMode = WalletMode.Normal;
                wallet.MagicCompletedAt = DateTime.UtcNow;

                var currentCycle = await _context.ClubMembershipCycles
                    .FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle,
                        context.CancellationToken);
                if (currentCycle != null)
                {
                    currentCycle.MagicCompletedAt = DateTime.UtcNow;
                    currentCycle.IsCurrentCycle = false;
                }

                // B6 fix: ریست PackagePurchaseMethod تا Guards G1-G3 خرید مجدد را مسدود نکنند
                if (user != null)
                {
                    user.PackagePurchaseMethod = PackagePurchaseMethod.None;
                }
                }
            }
            else if (wallet.WalletMode == WalletMode.Normal
                     && user != null
                     && user.PackagePurchaseMethod != PackagePurchaseMethod.None
                     && user.ClubMembership?.IsActive == true)
            {
                // ── ENTER Magic Mode ──
                wallet.WalletMode = WalletMode.Magic;
                wallet.MagicActivatedAt = DateTime.UtcNow;
                wallet.MagicCompletedAt = null;
                wallet.MagicTotalDeposited = 0;
                wallet.MagicTotalCredited = 0;

                var currentCycle = await _context.ClubMembershipCycles
                    .FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle,
                        context.CancellationToken);
                if (currentCycle != null)
                {
                    currentCycle.MagicStartedAt = DateTime.UtcNow;
                }
            }
            // ⚠️ اگه Magic باشه و Balance<=Threshold ولی سقف پر نشده → هنوز Magic!
            // کاربر میتونه دوباره شارژ کنه
        }

        // Create order
        var order = new UserOrder
        {
            UserId = userId,
            UserAddressId = defaultAddress.Id,
            Amount = totalAmount,
            TransactionId = transaction.Id,
            PaymentStatus = CMSMicroservice.Domain.Enums.PaymentStatus.Success,
            PaymentDate = DateTime.UtcNow,
            PaymentMethod = CMSMicroservice.Domain.Enums.PaymentMethod.Wallet,
            DeliveryStatus = CMSMicroservice.Domain.Enums.DeliveryStatus.Pending,
            HasVAT = true
        };
        
        _context.UserOrders.Add(order);
        await _context.SaveChangesAsync(context.CancellationToken);
        
        // Update transaction description with order ID
        transaction.Description = $"خرید محصولات - سفارش #{order.Id}";
        await _context.SaveChangesAsync(context.CancellationToken);
        
        // Create order VAT record
        var orderVat = new OrderVAT
        {
            OrderId = order.Id,
            VATRate = vatRate,
            BaseAmount = baseAmount,
            VATAmount = vatAmount,
            TotalAmount = totalAmount
        };
        
        _context.OrderVATs.Add(orderVat);
        
        // Create factor details for each cart item
        foreach (var cartItem in cartItems)
        {
            var factorDetail = new FactorDetails
            {
                OrderId = order.Id,
                ProductId = cartItem.ProductId,
                Count = cartItem.Count,
                UnitPrice = cartItem.Product.Price,
                UnitDiscount = 0,
                UnitDiscountPrice = cartItem.Product.Price,
                IsChangePrice = false
            };
            
            _context.FactorDetails.Add(factorDetail);
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);
        
        // Clear user's cart
        foreach (var cartItem in cartItems)
        {
            cartItem.IsDeleted = true;
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);

        // Deduct inventory and update SaleCount for each purchased item
        foreach (var cartItem in cartItems)
        {
            await _inventoryService.ConfirmSaleAsync(
                cartItem.ProductId, ProductType.RegularProduct,
                cartItem.Count, order.Id, context.CancellationToken);

            cartItem.Product.SaleCount += cartItem.Count;
        }

        await _context.SaveChangesAsync(context.CancellationToken);
        
        return new SubmitShopBuyOrderResponse
        {
            Id = order.Id
        };
    }
    
    [RequiresPermission(PermissionNames.OrdersCancel)]
    public override async Task<CancelOrderResponse> CancelOrder(CancelOrderRequest request, ServerCallContext context)
    {
        var command = new CancelOrderByAdminCommand
        {
            OrderId = request.OrderId,
            CancelReason = request.CancelReason,
            RefundToWallet = request.RefundPayment
        };
        
        await _sender.Send(command, context.CancellationToken);
        
        return new CancelOrderResponse
        {
            OrderId = request.OrderId,
            Status = (CMSMicroservice.Protobuf.Protos.DeliveryStatus)(int)Domain.Enums.DeliveryStatus.Cancelled,
            Message = "سفارش با موفقیت لغو شد",
            RefundProcessed = request.RefundPayment
        };
    }
    
    [RequiresPermission(PermissionNames.OrdersUpdate)]
    public override async Task<UpdateOrderStatusResponse> UpdateOrderStatus(UpdateOrderStatusRequest request, ServerCallContext context)
    {
        var order = await _context.UserOrders.FindAsync(new object[] { request.OrderId }, context.CancellationToken);
        if (order == null)
            throw new RpcException(new Status(StatusCode.NotFound, "سفارش یافت نشد"));
        
        var oldStatus = (int)order.DeliveryStatus;
        
        var command = new Application.OrderManagementCQ.Commands.UpdateOrderStatus.UpdateOrderStatusCommand
        {
            OrderId = request.OrderId,
            NewStatus = (Domain.Enums.DeliveryStatus)request.NewStatus
        };
        
        await _sender.Send(command, context.CancellationToken);
        
        return new UpdateOrderStatusResponse
        {
            Success = true,
            Message = "وضعیت سفارش با موفقیت تغییر کرد",
            OldStatus = oldStatus,
            NewStatus = request.NewStatus
        };
    }
    
    #region [ARCHIVED] GetOrdersByDateRange — تکراری: GetAllUserOrderByFilter همین قابلیت + فیلترهای بیشتر دارد
    [RequiresPermission(PermissionNames.ReportsView)]
    [Obsolete("ARCHIVED: Use GetAllUserOrderByFilter instead — has date range + more filters")]
    public override async Task<GetOrdersByDateRangeResponse> GetOrdersByDateRange(GetOrdersByDateRangeRequest request, ServerCallContext context)
    {
        var pageNumber = request.PageNumber > 0 ? request.PageNumber : 1;
        var pageSize = request.PageSize > 0 ? request.PageSize : 20;
        
        var query = _context.UserOrders
            .Include(o => o.User)
            .Include(o => o.FactorDetails)
            .Where(o => !o.IsDeleted);
        
        if (request.StartDate != null)
            query = query.Where(o => o.Created >= request.StartDate.ToDateTime());
        if (request.EndDate != null)
            query = query.Where(o => o.Created <= request.EndDate.ToDateTime());
        if (request.Status != null)
            query = query.Where(o => (int)o.DeliveryStatus == request.Status.Value);
        if (request.UserId != null)
            query = query.Where(o => o.UserId == request.UserId.Value);
        
        var totalCount = await query.CountAsync(context.CancellationToken);
        
        var orders = await query
            .OrderByDescending(o => o.Created)
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .Select(o => new OrderSummaryDto
            {
                OrderId = o.Id,
                OrderNumber = $"ORD-{o.Id:D6}",
                UserId = o.UserId,
                UserFullName = o.User != null ? (o.User.FirstName + " " + o.User.LastName) : string.Empty,
                TotalAmount = o.Amount,
                Status = (int)o.DeliveryStatus,
                StatusName = o.DeliveryStatus.ToString(),
                ItemsCount = o.FactorDetails.Count,
                CreatedAt = Timestamp.FromDateTime(DateTime.SpecifyKind(o.Created, DateTimeKind.Utc))
            })
            .ToListAsync(context.CancellationToken);
        
        return new GetOrdersByDateRangeResponse
        {
            MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
            {
                CurrentPage = pageNumber,
                PageSize = pageSize,
                TotalCount = totalCount,
                TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize),
                HasPrevious = pageNumber > 1,
                HasNext = pageNumber * pageSize < totalCount
            },
            Orders = { orders }
        };
    }
    #endregion
    
    [RequiresPermission(PermissionNames.OrdersUpdate)]
    public override async Task<ApplyDiscountToOrderResponse> ApplyDiscountToOrder(ApplyDiscountToOrderRequest request, ServerCallContext context)
    {
        var order = await _context.UserOrders.FindAsync(new object[] { request.OrderId }, context.CancellationToken);
        if (order == null)
            throw new RpcException(new Status(StatusCode.NotFound, "سفارش یافت نشد"));
        
        if (order.PaymentStatus == Domain.Enums.PaymentStatus.Success)
            return new ApplyDiscountToOrderResponse
            {
                Success = false,
                Message = "امکان اعمال تخفیف برای سفارش پرداخت شده وجود ندارد"
            };
        
        var originalAmount = order.Amount;
        var discountAmount = Math.Min(request.DiscountAmount, originalAmount);
        order.Amount = originalAmount - discountAmount;
        
        await _context.SaveChangesAsync(context.CancellationToken);
        
        return new ApplyDiscountToOrderResponse
        {
            Success = true,
            Message = $"تخفیف {discountAmount:N0} تومان با موفقیت اعمال شد",
            OriginalAmount = originalAmount,
            DiscountAmount = discountAmount,
            FinalAmount = order.Amount
        };
    }
    
    // [RequiresPermission(PermissionNames.OrdersView)]
    public override async Task<CalculateOrderPVResponse> CalculateOrderPV(CalculateOrderPVRequest request, ServerCallContext context)
    {
        var order = await _context.UserOrders
            .Include(o => o.FactorDetails)
                .ThenInclude(fd => fd.Product)
            .Where(o => o.Id == request.OrderId && !o.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (order == null)
            throw new RpcException(new Status(StatusCode.NotFound, "سفارش یافت نشد"));
        
        var products = new List<ProductPVDto>();
        long totalPV = 0;
        
        foreach (var fd in order.FactorDetails.Where(f => !f.IsDeleted))
        {
            // PV = UnitPrice * Count (simplified - can be customized)
            long unitPV = fd.UnitPrice;
            long itemPV = unitPV * fd.Count;
            totalPV += itemPV;
            
            products.Add(new ProductPVDto
            {
                ProductId = fd.ProductId,
                ProductTitle = fd.Product?.Title ?? string.Empty,
                Quantity = fd.Count,
                UnitPv = unitPV,
                TotalPv = itemPV
            });
        }
        
        return new CalculateOrderPVResponse
        {
            OrderId = request.OrderId,
            TotalPv = totalPV,
            Products = { products }
        };
    }
    
    // ============= Customer-specific Methods =============
    
    #region [ARCHIVED] CreateNewOrderForCustomer & SubmitOrderForCustomer — Stub خالی: مسیر سفارش مشتری از SubmitShopBuyOrder می‌گذرد
    [Obsolete("ARCHIVED: Empty stub — customer order flow uses SubmitShopBuyOrder")]
    public override async Task<CreateNewUserOrderResponse> CreateNewOrderForCustomer(CreateNewUserOrderRequest request, ServerCallContext context)
    {
        // [ARCHIVED] Empty stub — مسیر سفارش مشتری از SubmitShopBuyOrder می‌گذرد
        return new CreateNewUserOrderResponse();
    }
    
    [Obsolete("ARCHIVED: Empty stub — customer order flow uses SubmitShopBuyOrder")]
    public override async Task<SubmitShopBuyOrderResponse> SubmitOrderForCustomer(SubmitShopBuyOrderRequest request, ServerCallContext context)
    {
        // [ARCHIVED] Empty stub — مسیر سفارش مشتری از SubmitShopBuyOrder می‌گذرد
        return new SubmitShopBuyOrderResponse();
    }
    #endregion
    
    public override async Task<GetAllUserOrderByFilterResponse> GetCustomerOrders(GetAllUserOrderByFilterRequest request, ServerCallContext context)
    {
        // Customer endpoint: ALWAYS resolve userId from JWT (customer can only see own orders)
        var customerUserId = long.TryParse(_currentUserService.UserId, out var uid) ? uid : 0;
        if (customerUserId == 0)
            throw new RpcException(new Status(StatusCode.Unauthenticated, "کاربر احراز هویت نشده است"));

        var query = new GetCustomerOrdersQuery
        {
            UserId = customerUserId,
            PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
            PaymentStatusFilter = request.Filter?.HasPaymentStatus == true
                ? (int?)request.Filter.PaymentStatus
                : null,
            DeliveryStatusFilter = request.Filter?.HasDeliveryStatus == true
                ? (int?)request.Filter.DeliveryStatus
                : null,
            FromDate = request.Filter?.FromDate?.ToDateTime() ?? request.Filter?.PaymentDate?.ToDateTime(),
            ToDate = request.Filter?.ToDate?.ToDateTime()
        };

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

        var response = new GetAllUserOrderByFilterResponse
        {
            MetaData = result.MetaData.Adapt<MetaData>()
        };

        foreach (var model in result.Models)
        {
            var orderModel = new GetAllUserOrderByFilterResponseModel
            {
                Id = model.Id,
                Amount = model.Amount,
                PackageId = model.PackageId ?? 0,
                TransactionId = model.TransactionId,
                UserId = model.UserId,
                UserAddressId = model.UserAddressId,
                UserAddressText = model.UserAddressText,
                TrackingCode = model.TrackingCode,
                DeliveryDescription = model.DeliveryDescription,
                UserFullName = model.UserFullName,
                UserNationalCode = model.UserNationalCode,
                VatAmount = model.VatAmount,
                VatPercentage = model.VatPercentage
            };

            orderModel.PaymentStatus = (CMSMicroservice.Protobuf.Protos.PaymentStatus)model.PaymentStatus;
            if (model.PaymentDate.HasValue)
                orderModel.PaymentDate = Timestamp.FromDateTime(DateTime.SpecifyKind(model.PaymentDate.Value, DateTimeKind.Utc));
            
            if (model.PaymentMethod.HasValue)
                orderModel.PaymentMethod = (CMSMicroservice.Protobuf.Protos.PaymentMethod)model.PaymentMethod.Value;

            orderModel.DeliveryStatus = (CMSMicroservice.Protobuf.Protos.DeliveryStatus)model.DeliveryStatus;

            foreach (var fd in model.FactorDetails)
            {
                orderModel.FactorDetails.Add(new GetAllUserOrderByFilterResponseModelFactorDetail
                {
                    ProductId = fd.ProductId,
                    ProductTitle = fd.ProductTitle,
                    ProductThumbnailPath = fd.ProductThumbnailPath,
                    UnitPrice = fd.UnitPrice,
                    Count = fd.Count,
                    UnitDiscountPrice = fd.UnitDiscountPrice
                });
            }

            response.Models.Add(orderModel);
        }

        return response;
    }
    
    public override async Task<GetUserOrderResponse> GetCustomerOrder(GetUserOrderRequest request, ServerCallContext context)
    {
        // Customer endpoint: ALWAYS resolve userId from JWT (customer can only see own orders)
        var customerUserId = long.TryParse(_currentUserService.UserId, out var uid) ? uid : 0;
        if (customerUserId == 0)
            throw new RpcException(new Status(StatusCode.Unauthenticated, "کاربر احراز هویت نشده است"));

        var query = new GetCustomerOrderQuery
        {
            OrderId = request.Id,
            UserId = customerUserId
        };

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

        var response = new GetUserOrderResponse
        {
            Id = result.Id,
            Amount = result.Amount,
            PackageId = result.PackageId ?? 0,
            TransactionId = result.TransactionId,
            UserId = result.UserId,
            UserAddressId = result.UserAddressId,
            UserAddressText = result.UserAddressText,
            TrackingCode = result.TrackingCode,
            DeliveryDescription = result.DeliveryDescription,
            UserFullName = result.UserFullName,
            UserNationalCode = result.UserNationalCode,
            PostalCode = result.PostalCode,
            UserMobile = result.UserMobile
        };

        // VAT Info
        if (result.VatAmount > 0)
        {
            response.VatInfo = new OrderVATInfo
            {
                VatRate = result.VatPercentage / 100,
                BaseAmount = result.Amount - result.VatAmount,
                VatAmount = result.VatAmount,
                TotalAmount = result.Amount,
                IsPaid = result.PaymentStatus == Domain.Enums.PaymentStatus.Success
            };
        }

        response.PaymentStatus = (CMSMicroservice.Protobuf.Protos.PaymentStatus)result.PaymentStatus;
        if (result.PaymentDate.HasValue)
            response.PaymentDate = Timestamp.FromDateTime(DateTime.SpecifyKind(result.PaymentDate.Value, DateTimeKind.Utc));

        if (result.PaymentMethod.HasValue)
            response.PaymentMethod = (CMSMicroservice.Protobuf.Protos.PaymentMethod)result.PaymentMethod.Value;

        response.DeliveryStatus = (CMSMicroservice.Protobuf.Protos.DeliveryStatus)result.DeliveryStatus;

        foreach (var fd in result.FactorDetails)
        {
            response.FactorDetails.Add(new GetUserOrderResponseFactorDetail
            {
                ProductId = fd.ProductId,
                ProductTitle = fd.ProductTitle,
                ProductThumbnailPath = fd.ProductThumbnailPath,
                UnitPrice = fd.UnitPrice,
                Count = fd.Count,
                UnitDiscountPrice = fd.UnitDiscountPrice
            });
        }

        return response;
    }
    
    // ============= Customer-specific Method Implementations =============
    
    public override async Task<CustomerCancelOrderResponse> CustomerCancelOrder(CustomerCancelOrderRequest request, ServerCallContext context)
    {
        var order = await _context.UserOrders
            .Where(o => o.Id == request.OrderId && !o.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (order == null)
            return new CustomerCancelOrderResponse { Success = false, Message = "سفارش یافت نشد" };
        
        if (order.DeliveryStatus != Domain.Enums.DeliveryStatus.Pending)
            return new CustomerCancelOrderResponse { Success = false, Message = "فقط سفارش‌های در انتظار قابل لغو هستند" };
        
        var refundAmount = order.Amount;
        
        order.DeliveryStatus = Domain.Enums.DeliveryStatus.Cancelled;
        
        // Refund to wallet if payment was from wallet
        if (order.PaymentStatus == Domain.Enums.PaymentStatus.Success && order.PaymentMethod == Domain.Enums.PaymentMethod.Wallet)
        {
            var wallet = await _context.UserWallets
                .Where(w => w.UserId == order.UserId)
                .FirstOrDefaultAsync(context.CancellationToken);
            
            if (wallet != null)
            {
                wallet.Balance += refundAmount;
                _context.UserWalletHistories.Add(new UserWalletHistory
                {
                    WalletId = wallet.Id,
                    CurrentBalance = wallet.Balance,
                    ChangeValue = refundAmount,
                    CurrentNetworkBalance = wallet.NetworkBalance,
                    ChangeNerworkValue = 0,
                    CurrentDiscountBalance = wallet.DiscountBalance,
                    ChangeDiscountValue = 0,
                    IsIncrease = true,
                    RefrenceId = order.TransactionId ?? 0
                });
            }
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);
        
        return new CustomerCancelOrderResponse
        {
            Success = true,
            Message = "سفارش شما با موفقیت لغو شد",
            RefundAmount = refundAmount,
            RefundTransactionId = $"REF{order.Id}"
        };
    }
    
    public override async Task<GetCustomerOrderHistoryResponse> GetCustomerOrderHistory(GetCustomerOrderHistoryRequest request, ServerCallContext context)
    {
        var query = new GetCustomerOrderHistoryQuery
        {
            UserId = request.UserId,
            PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
            StatusFilter = request.StatusFilter != OrderStatusEnum.OrderStatusPending 
                ? (int?)request.StatusFilter 
                : null,
            FromDate = request.FromDate?.ToDateTime(),
            ToDate = request.ToDate?.ToDateTime()
        };

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

        var response = new GetCustomerOrderHistoryResponse
        {
            MetaData = result.MetaData.Adapt<MetaData>()
        };

        foreach (var order in result.Orders)
        {
            response.Orders.Add(new Protobuf.Protos.UserOrder.CustomerOrderModel
            {
                Id = order.Id,
                Amount = order.Amount,
                PackageId = order.PackageId ?? 0,
                PackageName = order.PackageName,
                Status = (OrderStatusEnum)order.Status,
                StatusMessage = order.StatusMessage,
                OrderDate = Timestamp.FromDateTime(DateTime.SpecifyKind(order.OrderDate, DateTimeKind.Utc)),
                DeliveryDate = order.DeliveryDate.HasValue 
                    ? Timestamp.FromDateTime(DateTime.SpecifyKind(order.DeliveryDate.Value, DateTimeKind.Utc))
                    : null,
                TrackingCode = order.TrackingCode,
                ItemsCount = order.ItemsCount,
                CanCancel = order.CanCancel,
                CanReorder = order.CanReorder
            });
        }

        return response;
    }
    
    public override async Task<CustomerTrackOrderResponse> CustomerTrackOrder(CustomerTrackOrderRequest request, ServerCallContext context)
    {
        var order = await _context.UserOrders
            .Include(o => o.FactorDetails)
            .Include(o => o.Package)
            .Where(o => o.Id == request.OrderId && !o.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (order == null)
            throw new RpcException(new Status(StatusCode.NotFound, "سفارش یافت نشد"));
        
        var orderModel = new Protobuf.Protos.UserOrder.CustomerOrderModel
        {
            Id = order.Id,
            Amount = order.Amount,
            PackageId = order.PackageId ?? 0,
            PackageName = order.Package?.Title ?? string.Empty,
            Status = (OrderStatusEnum)(int)order.DeliveryStatus,
            StatusMessage = GetDeliveryStatusPersian(order.DeliveryStatus),
            OrderDate = Timestamp.FromDateTime(DateTime.SpecifyKind(order.Created, DateTimeKind.Utc)),
            TrackingCode = order.TrackingCode ?? string.Empty,
            ItemsCount = order.FactorDetails.Count(f => !f.IsDeleted),
            CanCancel = order.DeliveryStatus == Domain.Enums.DeliveryStatus.Pending,
            CanReorder = order.DeliveryStatus == Domain.Enums.DeliveryStatus.Delivered
        };
        
        var response = new CustomerTrackOrderResponse
        {
            Order = orderModel,
            DeliveryInfo = new DeliveryTrackingInfo
            {
                TrackingCode = order.TrackingCode ?? string.Empty,
                CourierName = "پست پیشتاز",
                EstimatedDelivery = order.DeliveryDescription ?? string.Empty,
                CurrentLocation = string.Empty
            }
        };
        
        // Add current status to history
        response.StatusHistory.Add(new OrderStatusHistory
        {
            Status = (OrderStatusEnum)(int)order.DeliveryStatus,
            StatusMessage = GetDeliveryStatusPersian(order.DeliveryStatus),
            ChangedAt = order.LastModified.HasValue 
                ? Timestamp.FromDateTime(DateTime.SpecifyKind(order.LastModified.Value, DateTimeKind.Utc))
                : Timestamp.FromDateTime(DateTime.SpecifyKind(order.Created, DateTimeKind.Utc)),
            ChangedBy = "سیستم"
        });
        
        return response;
    }
    
    public override async Task<CustomerReorderResponse> CustomerReorderPreviousOrder(CustomerReorderRequest request, ServerCallContext context)
    {
        var originalOrder = await _context.UserOrders
            .Include(o => o.FactorDetails)
                .ThenInclude(fd => fd.Product)
            .Where(o => o.Id == request.OriginalOrderId && !o.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (originalOrder == null)
            return new CustomerReorderResponse { Success = false, Message = "سفارش اصلی یافت نشد" };
        
        var userId = originalOrder.UserId;
        
        // Add items from old order to cart
        foreach (var fd in originalOrder.FactorDetails.Where(f => !f.IsDeleted))
        {
            var existingCartItem = await _context.UserCarts
                .Where(c => c.UserId == userId && c.ProductId == fd.ProductId && !c.IsDeleted)
                .FirstOrDefaultAsync(context.CancellationToken);
            
            if (existingCartItem != null)
            {
                existingCartItem.Count += fd.Count;
            }
            else
            {
                _context.UserCarts.Add(new UserCart
                {
                    UserId = userId,
                    ProductId = fd.ProductId,
                    Count = fd.Count
                });
            }
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);
        
        // Calculate total with current prices
        long totalAmount = originalOrder.FactorDetails
            .Where(f => !f.IsDeleted)
            .Sum(fd => request.UseCurrentPrices && fd.Product != null 
                ? fd.Product.Price * fd.Count 
                : fd.UnitPrice * fd.Count);
        
        return new CustomerReorderResponse
        {
            Success = true,
            Message = "محصولات به سبد خرید اضافه شدند",
            NewOrderId = 0, // Cart items added, no order created yet
            TotalAmount = totalAmount
        };
    }
    
    public override Task<GetVATRateResponse> GetVATRate(Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context)
    {
        // VAT Rate for Iran: 10% (نرخ مالیات بر ارزش افزوده ایران)
        return Task.FromResult(new GetVATRateResponse
        {
            VatRate = 0.10,
            VatPercentage = 10,
            IsEnabled = true
        });
    }
    
    // ============= Helper Methods =============
    
    private static string GetDeliveryStatusPersian(Domain.Enums.DeliveryStatus status) => status switch
    {
        Domain.Enums.DeliveryStatus.None => "نامشخص",
        Domain.Enums.DeliveryStatus.Pending => "در انتظار ارسال",
        Domain.Enums.DeliveryStatus.InTransit => "در حال ارسال",
        Domain.Enums.DeliveryStatus.Delivered => "تحویل داده شده",
        Domain.Enums.DeliveryStatus.Returned => "مرجوع شده",
        Domain.Enums.DeliveryStatus.Cancelled => "لغو شده",
        Domain.Enums.DeliveryStatus.ReadyForOfficePickup => "آماده تحویل در دفتر",
        _ => status.ToString()
    };
}
