using CMSMicroservice.Protobuf.Protos.Transactions;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.TransactionsCQ.Commands.CreateNewTransactions;
using CMSMicroservice.Application.TransactionsCQ.Commands.UpdateTransactions;
using CMSMicroservice.Application.TransactionsCQ.Commands.DeleteTransactions;
using CMSMicroservice.Application.TransactionsCQ.Queries.GetTransactions;
using CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
using CMSMicroservice.Application.TransactionsCQ.Queries.GetTransactionSummary;
using CMSMicroservice.Application.TransactionsCQ.Commands.VerifyTransaction;
using CMSMicroservice.Application.TransactionsCQ.Commands.RefundTransaction;
using CMSMicroservice.Application.TransactionsCQ.Queries.GetCustomerTransaction;
using CMSMicroservice.Application.TransactionsCQ.Queries.GetCustomerTransactionsByFilter;
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 MediatR;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System.Linq;

namespace CMSMicroservice.WebApi.Services;
public class TransactionsService : TransactionsContract.TransactionsContractBase
{
    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 TransactionsService(
        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<CreateNewTransactionsResponse> CreateNewTransactions(CreateNewTransactionsRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<CreateNewTransactionsRequest, CreateNewTransactionsCommand, CreateNewTransactionsResponse>(request, context);
    }
    public override async Task<Empty> UpdateTransactions(UpdateTransactionsRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<UpdateTransactionsRequest, UpdateTransactionsCommand>(request, context);
    }
    public override async Task<Empty> DeleteTransactions(DeleteTransactionsRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<DeleteTransactionsRequest, DeleteTransactionsCommand>(request, context);
    }
    public override async Task<GetTransactionsResponse> GetTransactions(GetTransactionsRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetTransactionsRequest, GetTransactionsQuery, GetTransactionsResponse>(request, context);
    }
    public override async Task<GetAllTransactionsByFilterResponse> GetAllTransactionsByFilter(GetAllTransactionsByFilterRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetAllTransactionsByFilterRequest, GetAllTransactionsByFilterQuery, GetAllTransactionsByFilterResponse>(request, context);
    }

    public override Task<GetTransactionSummaryResponse> GetTransactionSummary(
        GetTransactionSummaryRequest request, ServerCallContext context) =>
        _dispatchRequestToCQRS.Handle<GetTransactionSummaryRequest, GetTransactionSummaryQuery, GetTransactionSummaryResponse>(request, context);
    
    public override async Task<VerifyTransactionResponse> VerifyTransaction(VerifyTransactionRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<VerifyTransactionRequest, VerifyTransactionCommand, VerifyTransactionResponse>(request, context);
    }
    
    public override async Task<RefundTransactionResponse> RefundTransaction(RefundTransactionRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<RefundTransactionRequest, RefundTransactionCommand, RefundTransactionResponse>(request, context);
    }
    
    // ============= Customer-specific Methods =============
    
    public override async Task<GetCustomerTransactionResponse> GetCustomerTransaction(GetCustomerTransactionRequest request, ServerCallContext context)
    {
        var query = new GetCustomerTransactionQuery
        {
            Id = request.Id,
            Authority = request.Authority,
            UserId = 0 // از JWT دریافت می‌شود
        };

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

        return new GetCustomerTransactionResponse
        {
            Id = result.Id,
            Amount = result.Amount,
            Description = result.Description,
            PaymentStatus = result.PaymentStatus == Domain.Enums.PaymentStatus.Success,
            RefId = result.RefId,
            Type = (TransactionTypeEnum)result.Type,
            Currency = CurrencyEnum.Irr
        };
    }
    
    public override async Task<GetCustomerTransactionsByFilterResponse> GetCustomerTransactionsByFilter(GetCustomerTransactionsByFilterRequest request, ServerCallContext context)
    {
        var query = new GetCustomerTransactionsByFilterQuery
        {
            UserId = 0, // از JWT دریافت می‌شود
            PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
            SortBy = request.SortBy,
            IdFilter = request.Filter?.Id,
            AmountFilter = request.Filter?.Amount,
            DescriptionFilter = request.Filter?.Description,
            PaymentStatusFilter = request.Filter?.PaymentStatus,
            RefIdFilter = request.Filter?.RefId,
            TypeFilter = request.Filter?.Type != null ? (int?)request.Filter.Type : null
        };

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

        var response = new GetCustomerTransactionsByFilterResponse
        {
            MetaData = result.MetaData.Adapt<CMSMicroservice.Protobuf.Protos.MetaData>()
        };

        foreach (var model in result.Models)
        {
            response.Models.Add(new GetCustomerTransactionsByFilterResponseModel
            {
                Id = model.Id,
                Amount = model.Amount,
                Description = model.Description,
                PaymentStatus = model.PaymentStatus == Domain.Enums.PaymentStatus.Success,
                RefId = model.RefId,
                Type = (TransactionTypeEnum)model.Type,
                Currency = CurrencyEnum.Irr
            });
        }

        return response;
    }
    
    public override async Task<CustomerPaymentRequestResponse> CustomerPaymentRequest(CustomerPaymentRequestRequest request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();

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

    private async Task<CustomerPaymentRequestResponse> CustomerPaymentRequestCore(
        CustomerPaymentRequestRequest request,
        long userId,
        CancellationToken cancellationToken)
    {
        // Get user mobile for payment gateway
        var user = await _context.Users
            .AsNoTracking()
            .Where(u => u.Id == userId)
            .Select(u => new { u.Mobile, u.Email })
            .FirstOrDefaultAsync(cancellationToken);
        
        // Create transaction record in DB
        var transaction = new CMSMicroservice.Domain.Entities.Transaction
        {
            Amount = request.Amount,
            Description = request.Description ?? "پرداخت آنلاین",
            PaymentStatus = Domain.Enums.PaymentStatus.Pending,
            Type = Domain.Enums.TransactionType.DepositIpg
        };
        
        _context.Transactions.Add(transaction);
        await _context.SaveChangesAsync(cancellationToken);
        
        // Callback URL از config — نه از ورودی کاربر (امنیت)
        var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268";
        var callbackUrl = $"{frontOfficeBaseUrl}/profile/payment-callback";

        // Initiate payment with gateway
        var paymentResult = await _paymentGateway.InitiatePaymentAsync(new PaymentRequest
        {
            Amount = request.Amount,
            UserId = userId,
            Mobile = request.Mobile ?? user?.Mobile ?? string.Empty,
            Description = request.Description ?? "پرداخت آنلاین",
            CallbackUrl = callbackUrl
        }, cancellationToken);
        
        if (!paymentResult.IsSuccess)
        {
            // Update transaction status to failed
            transaction.PaymentStatus = Domain.Enums.PaymentStatus.Reject;
            await _context.SaveChangesAsync(cancellationToken);
            
            throw new RpcException(new Status(StatusCode.Internal, 
                paymentResult.ErrorMessage ?? "خطا در ارتباط با درگاه پرداخت"));
        }
        
        // Save RefId from gateway
        transaction.RefId = paymentResult.RefId;
        
        // ثبت PaymentTransaction
        var paymentTx = new PaymentTransaction
        {
            GatewayProvider = _configuration["PaymentProvider"] ?? "zarinpal",
            MerchantId = _configuration["ZarinPal:MerchantId"] ?? "",
            Amount = request.Amount,
            CallbackUrl = callbackUrl,
            Description = request.Description ?? "پرداخت آنلاین",
            Mobile = request.Mobile ?? user?.Mobile,
            UserId = userId,
            RequestStatusCode = 100,
            RequestStatusMessage = "Success",
            Authority = paymentResult.RefId,
            PaymentStatus = false,
            TransactionId = transaction.Id
        };
        _context.PaymentTransactions.Add(paymentTx);
        await _context.SaveChangesAsync(cancellationToken);
        
        return new CustomerPaymentRequestResponse
        {
            PaymentGWUrl = paymentResult.GatewayUrl ?? string.Empty
        };
    }
    
    public override async Task<CustomerPaymentVerificationResponse> CustomerPaymentVerification(CustomerPaymentVerificationRequest request, ServerCallContext context)
    {
        var ct = context.CancellationToken;
        var userId = GetCurrentUserId();
        var verifyKey = !string.IsNullOrEmpty(request.Authority)
            ? request.Authority
            : userId.ToString();

        try
        {
            return await _paymentLock.ExecuteAsync(
                PaymentLockScopes.Verify(userId, verifyKey),
                PaymentLockStrategy.WaitForRelease,
                lockCt => CustomerPaymentVerificationCore(request, lockCt),
                ct);
        }
        catch (PaymentInProgressException ex)
        {
            throw new RpcException(new Status(StatusCode.FailedPrecondition, ex.Message));
        }
    }

    private async Task<CustomerPaymentVerificationResponse> CustomerPaymentVerificationCore(
        CustomerPaymentVerificationRequest request,
        CancellationToken cancellationToken)
    {
        // Find the transaction by authority/refId
        var transaction = await _context.Transactions
            .Where(t => t.RefId == request.Authority && !t.IsDeleted)
            .FirstOrDefaultAsync(cancellationToken);
        
        if (transaction == null)
            throw new RpcException(new Status(StatusCode.NotFound, "تراکنش یافت نشد"));
        
        // If status from gateway callback is not OK, mark as rejected
        if (request.Status != "OK")
        {
            transaction.PaymentStatus = Domain.Enums.PaymentStatus.Reject;
            await _context.SaveChangesAsync(cancellationToken);
            
            return new CustomerPaymentVerificationResponse
            {
                Id = transaction.Id,
                PaymentStatus = false,
                Message = "پرداخت توسط کاربر لغو شد",
                VerificationStatusCode = -1
            };
        }
        
        // واکشی PaymentTransaction برای گرفتن مبلغ (تومان) جهت verify
        var paymentTx = await _context.PaymentTransactions
            .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, cancellationToken);
        
        var amountInToman = paymentTx?.Amount ?? transaction.Amount;
        
        // Verify with gateway (مبلغ به تومان — سرویس زرین‌پال خودش ×۱۰ می‌کنه)
        var verifyResult = await _paymentGateway.VerifyPaymentAsync(
            request.Authority, request.Status, (decimal)amountInToman, 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.PaymentStatus = Domain.Enums.PaymentStatus.Success;
            transaction.PaymentDate = DateTime.UtcNow;
            transaction.RefId = verifyResult.RefId;
        }
        else
        {
            transaction.PaymentStatus = Domain.Enums.PaymentStatus.Reject;
        }
        
        await _context.SaveChangesAsync(cancellationToken);
        
        return new CustomerPaymentVerificationResponse
        {
            Id = transaction.Id,
            PaymentStatus = verifyResult.IsSuccess,
            Message = verifyResult.IsSuccess ? "پرداخت با موفقیت انجام شد" : (verifyResult.Message ?? "پرداخت ناموفق"),
            RefId = verifyResult.RefId ?? string.Empty,
            OrderId = string.Empty,
            VerificationStatusCode = verifyResult.IsSuccess ? 100 : -1
        };
    }
    
    private long GetCurrentUserId()
    {
        if (long.TryParse(_currentUserService.UserId, out var userId) && userId > 0)
            return userId;
        throw new RpcException(new Status(StatusCode.Unauthenticated, "لطفاً وارد حساب کاربری خود شوید"));
    }
}
