using CMSMicroservice.Protobuf.Protos.Package;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.PackageCQ.Commands.CreateNewPackage;
using CMSMicroservice.Application.PackageCQ.Commands.UpdatePackage;
using CMSMicroservice.Application.PackageCQ.Commands.DeletePackage;
using CMSMicroservice.Application.PackageCQ.Queries.GetPackage;
using CMSMicroservice.Application.PackageCQ.Queries.GetAllPackageByFilter;
using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus;
using CMSMicroservice.Application.PackageCQ.Queries.GetCustomerPackages;
using CMSMicroservice.Application.PackageCQ.Queries.GetCustomerPackageDetails;
using CMSMicroservice.Application.PackageCQ.Queries.GetCustomerPurchaseHistory;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Entities.Payment;
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 Microsoft.EntityFrameworkCore;
using MediatR;
using Mapster;
using Microsoft.Extensions.Configuration;

namespace CMSMicroservice.WebApi.Services;
public class PackageService : PackageContract.PackageContractBase
{
    private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
    private readonly ISender _sender;
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUserService;
    private readonly IPaymentGatewayService _paymentGateway;
    private readonly IConfiguration _configuration;

    public PackageService(
        IDispatchRequestToCQRS dispatchRequestToCQRS, 
        ISender sender,
        IApplicationDbContext context,
        ICurrentUserService currentUserService,
        IPaymentGatewayService paymentGateway,
        IConfiguration configuration)
    {
        _dispatchRequestToCQRS = dispatchRequestToCQRS;
        _sender = sender;
        _context = context;
        _currentUserService = currentUserService;
        _paymentGateway = paymentGateway;
        _configuration = configuration;
    }
    public override async Task<CreateNewPackageResponse> CreateNewPackage(CreateNewPackageRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<CreateNewPackageRequest, CreateNewPackageCommand, CreateNewPackageResponse>(request, context);
    }
    public override async Task<Empty> UpdatePackage(UpdatePackageRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<UpdatePackageRequest, UpdatePackageCommand>(request, context);
    }
    public override async Task<Empty> DeletePackage(DeletePackageRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<DeletePackageRequest, DeletePackageCommand>(request, context);
    }
    public override async Task<GetPackageResponse> GetPackage(GetPackageRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetPackageRequest, GetPackageQuery, GetPackageResponse>(request, context);
    }
    public override async Task<GetAllPackageByFilterResponse> GetAllPackageByFilter(GetAllPackageByFilterRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetAllPackageByFilterRequest, GetAllPackageByFilterQuery, GetAllPackageByFilterResponse>(request, context);
    }
    
    public override async Task<GetUserPackageStatusResponse> GetUserPackageStatus(GetUserPackageStatusRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetUserPackageStatusRequest, GetUserPackageStatusQuery, GetUserPackageStatusResponse>(request, context);
    }
    
    // ============= Customer-specific Method Implementations =============
    
    public override async Task<GetCustomerPackagesResponse> GetCustomerPackages(GetCustomerPackagesRequest request, ServerCallContext context)
    {
        var query = new GetCustomerPackagesQuery
        {
            IncludeInactive = request.IncludeInactive,
            PackageTypeFilter = request.PackageTypeFilter != PackageTypeEnum.PackageTypeBasic 
                ? (int?)request.PackageTypeFilter 
                : null
        };

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

        var response = new GetCustomerPackagesResponse();
        response.Models.AddRange(result.Adapt<List<CustomerPackageModel>>());

        return response;
    }
    
    public override async Task<GetCustomerPackageDetailsResponse> GetCustomerPackageDetails(GetCustomerPackageDetailsRequest request, ServerCallContext context)
    {
        var query = new GetCustomerPackageDetailsQuery
        {
            PackageId = request.PackageId
        };

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

        var response = new GetCustomerPackageDetailsResponse
        {
            Id = result.Id,
            Title = result.Title,
            Description = result.Description,
            Price = result.Price,
            ImagePath = result.ImagePath
        };

        // Map Features
        foreach (var feature in result.Features)
        {
            response.Features.Add(new PackageFeature
            {
                Title = feature.Title,
                Description = feature.Description,
                Icon = feature.Icon,
                IsHighlighted = feature.IsHighlighted
            });
        }

        // Map Requirements
        response.Requirements = new PurchaseRequirements
        {
            RequiresMembership = result.Requirements.RequiresMembership,
            MinimumWalletBalance = result.Requirements.MinimumWalletBalance
        };
        response.Requirements.Restrictions.AddRange(result.Requirements.Restrictions);

        return response;
    }
    
    public override async Task<CustomerPurchasePackageResponse> CustomerPurchasePackage(CustomerPurchasePackageRequest request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();
        
        // Lookup package
        var package = await _context.Packages
            .AsNoTracking()
            .Where(p => p.Id == request.PackageId && !p.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (package == null)
            throw new RpcException(new Status(StatusCode.NotFound, "پکیج مورد نظر یافت نشد"));
        
        // Create transaction
        var transaction = new CMSMicroservice.Domain.Entities.Transaction
        {
            Amount = package.Price,
            Description = $"خرید پکیج {package.Title}",
            PaymentStatus = Domain.Enums.PaymentStatus.Pending,
            Type = Domain.Enums.TransactionType.Buy
        };
        _context.Transactions.Add(transaction);
        await _context.SaveChangesAsync(context.CancellationToken);
        
        // Create purchase record
        var purchaseMethod = request.PurchaseMethod == PurchaseMethodEnum.PurchaseMethodGateway 
            ? Domain.Enums.PackagePurchaseMethod.DirectPurchase 
            : Domain.Enums.PackagePurchaseMethod.DayaLoan;
        
        var purchase = new CMSMicroservice.Domain.Entities.UserPackagePurchase
        {
            UserId = userId,
            PackageId = package.Id,
            PurchaseMethod = purchaseMethod,
            PurchasedAt = DateTime.UtcNow,
            Amount = package.Price,
            TransactionId = transaction.Id
        };
        _context.UserPackagePurchases.Add(purchase);
        await _context.SaveChangesAsync(context.CancellationToken);
        
        // Initiate payment with gateway
        var user = await _context.Users
            .AsNoTracking()
            .Where(u => u.Id == userId)
            .Select(u => new { u.Mobile })
            .FirstOrDefaultAsync(context.CancellationToken);
        
        // Callback URL از config — نه از ورودی کاربر (امنیت)
        var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268";
        var callbackUrl = $"{frontOfficeBaseUrl}/profile/payment-callback?orderId={purchase.Id}";
        
        var paymentResult = await _paymentGateway.InitiatePaymentAsync(new PaymentRequest
        {
            Amount = package.Price,
            UserId = userId,
            Mobile = user?.Mobile ?? string.Empty,
            Description = $"خرید پکیج {package.Title}",
            CallbackUrl = callbackUrl
        }, context.CancellationToken);
        
        if (!paymentResult.IsSuccess)
        {
            transaction.PaymentStatus = Domain.Enums.PaymentStatus.Reject;
            await _context.SaveChangesAsync(context.CancellationToken);
            
            return new CustomerPurchasePackageResponse
            {
                Success = false,
                Message = paymentResult.ErrorMessage ?? "خطا در ارتباط با درگاه پرداخت"
            };
        }
        
        // Save RefId
        transaction.RefId = paymentResult.RefId;

        // ثبت PaymentTransaction
        var paymentTx = new PaymentTransaction
        {
            GatewayProvider = _configuration["PaymentProvider"] ?? "zarinpal",
            MerchantId = _configuration["ZarinPal:MerchantId"] ?? "",
            Amount = package.Price,
            CallbackUrl = callbackUrl,
            Description = $"خرید پکیج {package.Title}",
            Mobile = user?.Mobile,
            UserId = userId,
            RequestStatusCode = 100,
            RequestStatusMessage = "Success",
            Authority = paymentResult.RefId,
            PaymentStatus = false,
            TransactionId = transaction.Id,
            OrderId = purchase.Id.ToString()
        };
        _context.PaymentTransactions.Add(paymentTx);
        await _context.SaveChangesAsync(context.CancellationToken);
        
        return new CustomerPurchasePackageResponse
        {
            Success = true,
            Message = "درخواست خرید پکیج با موفقیت ثبت شد",
            OrderId = purchase.Id,
            PaymentGatewayUrl = paymentResult.GatewayUrl ?? string.Empty,
            Authority = paymentResult.RefId ?? string.Empty
        };
    }
    
    public override async Task<CustomerVerifyPackagePurchaseResponse> CustomerVerifyPackagePurchase(CustomerVerifyPackagePurchaseRequest request, ServerCallContext context)
    {
        // Find purchase record
        var purchase = await _context.UserPackagePurchases
            .Include(p => p.Package)
            .Where(p => p.Id == request.OrderId && !p.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (purchase == null)
            throw new RpcException(new Status(StatusCode.NotFound, "سفارش یافت نشد"));
        
        // Find the associated transaction
        var transaction = purchase.TransactionId.HasValue
            ? await _context.Transactions
                .Where(t => t.Id == purchase.TransactionId.Value)
                .FirstOrDefaultAsync(context.CancellationToken)
            : null;
        
        // If status from gateway callback is not OK
        if (request.Status != "OK")
        {
            if (transaction != null)
            {
                transaction.PaymentStatus = Domain.Enums.PaymentStatus.Reject;
                await _context.SaveChangesAsync(context.CancellationToken);
            }
            
            return new CustomerVerifyPackagePurchaseResponse
            {
                Success = false,
                Message = "پرداخت توسط کاربر لغو شد"
            };
        }
        
        // واکشی PaymentTransaction برای گرفتن مبلغ (تومان) جهت verify
        var paymentTx = await _context.PaymentTransactions
            .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, context.CancellationToken);
        
        var amountInToman = paymentTx?.Amount ?? purchase.Amount;
        
        // Verify with payment gateway (مبلغ به تومان — سرویس زرین‌پال خودش ×۱۰ می‌کنه)
        var verifyResult = await _paymentGateway.VerifyPaymentAsync(
            request.Authority, request.Status, (decimal)amountInToman, context.CancellationToken);
        if (paymentTx != null)
        {
            paymentTx.PaymentStatus = verifyResult.IsSuccess;
            paymentTx.VerificationStatusCode = verifyResult.VerificationCode;
            paymentTx.VerificationStatusMessage = verifyResult.Message;
            paymentTx.CardPan = verifyResult.CardPan;
            paymentTx.CardHash = verifyResult.CardHash;
            paymentTx.RefId = verifyResult.TrackingCode;
        }
        
        if (verifyResult.IsSuccess && transaction != null)
        {
            transaction.PaymentStatus = Domain.Enums.PaymentStatus.Success;
            transaction.PaymentDate = DateTime.UtcNow;
            transaction.RefId = verifyResult.RefId;

            // شارژ کیف پول کاربر
            var wallet = await _context.UserWallets
                .FirstOrDefaultAsync(w => w.UserId == purchase.UserId, context.CancellationToken);

            if (wallet == null)
            {
                wallet = new CMSMicroservice.Domain.Entities.UserWallet
                {
                    UserId = purchase.UserId,
                    Balance = 0,
                    DiscountBalance = 0,
                    NetworkBalance = 0
                };
                _context.UserWallets.Add(wallet);
                await _context.SaveChangesAsync(context.CancellationToken);
            }

            var discountMultiplier = purchase.Package?.DiscountMultiplier
                ?? throw new RpcException(new Status(StatusCode.Internal, "پکیج سفارش یافت نشد"));
            var discountAmount = (long)(purchase.Amount * (double)discountMultiplier);
            wallet.Balance += purchase.Amount;
            wallet.DiscountBalance += discountAmount;

            // ثبت لاگ کیف پول
            var walletLog = new CMSMicroservice.Domain.Entities.UserWalletHistory
            {
                WalletId = wallet.Id,
                CurrentBalance = wallet.Balance,
                ChangeValue = purchase.Amount,
                CurrentNetworkBalance = wallet.NetworkBalance,
                ChangeNerworkValue = 0,
                CurrentDiscountBalance = wallet.DiscountBalance,
                ChangeDiscountValue = discountAmount,
                IsIncrease = true,
                RefrenceId = transaction.Id,
                PackageId = purchase.PackageId
            };
            _context.UserWalletHistories.Add(walletLog);

            // به‌روزرسانی کاربر
            var user = await _context.Users
                .FirstOrDefaultAsync(u => u.Id == purchase.UserId, context.CancellationToken);
            if (user != null)
                user.PackagePurchaseMethod = Domain.Enums.PackagePurchaseMethod.DirectPurchase;
        }
        else if (transaction != null)
        {
            transaction.PaymentStatus = Domain.Enums.PaymentStatus.Reject;
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);
        
        // فعالسازی خودکار باشگاه مشتریان بعد از تأیید پرداخت موفق
        if (verifyResult.IsSuccess)
        {
            try
            {
                await _sender.Send(new CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership.ActivateClubMembershipCommand
                {
                    UserId = purchase.UserId,
                    ForceActivation = false
                }, context.CancellationToken);
            }
            catch (Exception ex)
            {
                // لاگ خطا ولی پرداخت موفق بوده — کاربر می‌تونه دستی فعالسازی کنه
                System.Console.WriteLine($"Auto club activation failed for UserId {purchase.UserId}: {ex.Message}");
            }
        }
        
        return new CustomerVerifyPackagePurchaseResponse
        {
            Success = verifyResult.IsSuccess,
            Message = verifyResult.IsSuccess ? "خرید پکیج با موفقیت تایید شد" : (verifyResult.Message ?? "خرید پکیج ناموفق بود"),
            TransactionId = transaction?.Id ?? 0,
            ReferenceCode = verifyResult.RefId ?? string.Empty,
            PurchaseInfo = verifyResult.IsSuccess ? new PackagePurchaseInfo
            {
                PackageId = purchase.PackageId,
                PackageName = purchase.Package?.Title ?? string.Empty,
                AmountPaid = purchase.Amount,
                PurchaseDate = Timestamp.FromDateTime(DateTime.SpecifyKind(purchase.PurchasedAt, DateTimeKind.Utc))
            } : null
        };
    }
    
    private long GetCurrentUserId()
    {
        if (long.TryParse(_currentUserService.UserId, out var userId) && userId > 0)
            return userId;
        throw new RpcException(new Status(StatusCode.Unauthenticated, "لطفاً وارد حساب کاربری خود شوید"));
    }
    
    public override async Task<GetCustomerPurchaseHistoryResponse> GetCustomerPurchaseHistory(GetCustomerPurchaseHistoryRequest request, ServerCallContext context)
    {
        var query = new GetCustomerPurchaseHistoryQuery
        {
            UserId = request.UserId,
            PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
            PackageTypeFilter = request.PackageTypeFilter != PackageTypeEnum.PackageTypeBasic 
                ? (int?)request.PackageTypeFilter 
                : null,
            FromDate = request.FromDate?.ToDateTime(),
            ToDate = request.ToDate?.ToDateTime()
        };

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

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

        foreach (var purchase in result.Purchases)
        {
            response.Purchases.Add(new PackagePurchaseHistory
            {
                Id = purchase.Id,
                PackageId = purchase.PackageId,
                PackageName = purchase.PackageName,
                Amount = purchase.Amount,
                PackageType = (PackageTypeEnum)purchase.PackageType,
                PurchaseDate = Timestamp.FromDateTime(DateTime.SpecifyKind(purchase.PurchaseDate, DateTimeKind.Utc)),
                ExpiryDate = purchase.ExpiryDate.HasValue 
                    ? Timestamp.FromDateTime(DateTime.SpecifyKind(purchase.ExpiryDate.Value, DateTimeKind.Utc))
                    : null,
                Status = (PaymentStatusEnum)purchase.Status,
                StatusMessage = purchase.StatusMessage,
                ReferenceCode = purchase.ReferenceCode
            });
        }

        return response;
    }
}
