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.PackageCQ.Commands.VerifyUserPackagePurchasePayment;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.Common;
using CMSMicroservice.Application.Common.Exceptions;
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;
    private readonly IUserPaymentLock _paymentLock;

    public PackageService(
        IDispatchRequestToCQRS dispatchRequestToCQRS, 
        ISender sender,
        IApplicationDbContext context,
        ICurrentUserService currentUserService,
        IPaymentGatewayService paymentGateway,
        IConfiguration configuration,
        IUserPaymentLock paymentLock)
    {
        _dispatchRequestToCQRS = dispatchRequestToCQRS;
        _sender = sender;
        _context = context;
        _currentUserService = currentUserService;
        _paymentGateway = paymentGateway;
        _configuration = configuration;
        _paymentLock = paymentLock;
    }
    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();

        try
        {
            return await _paymentLock.ExecuteAsync(
                PaymentLockScopes.Initiate(userId),
                PaymentLockStrategy.FailFast,
                ct => CustomerPurchasePackageCore(request, userId, ct),
                context.CancellationToken);
        }
        catch (PaymentInProgressException ex)
        {
            throw new RpcException(new Status(StatusCode.FailedPrecondition, ex.Message));
        }
    }

    private async Task<CustomerPurchasePackageResponse> CustomerPurchasePackageCore(
        CustomerPurchasePackageRequest request,
        long userId,
        CancellationToken cancellationToken)
    {
        // Lookup package
        var package = await _context.Packages
            .AsNoTracking()
            .Where(p => p.Id == request.PackageId && !p.IsDeleted)
            .FirstOrDefaultAsync(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(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(cancellationToken);
        
        // Initiate payment with gateway
        var user = await _context.Users
            .AsNoTracking()
            .Where(u => u.Id == userId)
            .Select(u => new { u.Mobile })
            .FirstOrDefaultAsync(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
        }, cancellationToken);
        
        if (!paymentResult.IsSuccess)
        {
            transaction.PaymentStatus = Domain.Enums.PaymentStatus.Reject;
            await _context.SaveChangesAsync(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(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)
    {
        try
        {
            var result = await _sender.Send(new VerifyUserPackagePurchasePaymentCommand
            {
                PurchaseId = request.OrderId,
                Authority = request.Authority ?? string.Empty,
                Status = request.Status ?? "NOK"
            }, context.CancellationToken);

            return MapVerifyPackagePurchaseResponse(result);
        }
        catch (PaymentInProgressException ex)
        {
            throw new RpcException(new Status(StatusCode.FailedPrecondition, ex.Message));
        }
    }

    private static CustomerVerifyPackagePurchaseResponse MapVerifyPackagePurchaseResponse(
        VerifyUserPackagePurchasePaymentResult result)
    {
        return new CustomerVerifyPackagePurchaseResponse
        {
            Success = result.Success,
            Message = result.Message,
            TransactionId = result.TransactionId,
            ReferenceCode = result.ReferenceCode,
            PurchaseInfo = result.Success
                ? new PackagePurchaseInfo
                {
                    PackageId = result.PackageId,
                    PackageName = result.PackageName,
                    AmountPaid = result.AmountPaid,
                    PurchaseDate = Timestamp.FromDateTime(
                        DateTime.SpecifyKind(result.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;
    }
}
