using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.PaymentGetway;
using Marco.Pms.Model.ViewModels.PaymentGetway;
using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Service;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Razorpay.Api;
using System.Security.Cryptography;
using System.Text;

namespace Marco.Pms.Services.Service
{
    public class RazorpayService : IRazorpayService
    {
        private readonly ApplicationDbContext _context;
        private readonly RazorpayClient _razorpayClient;
        private readonly ILoggingService _logger;
        private readonly IConfiguration _configuration;
        private readonly IAesEncryption _aesEncryption;
        private readonly string key;
        private readonly string secret;
        private readonly byte[] encryptionKey;

        public RazorpayService(ApplicationDbContext context, IConfiguration configuration, ILoggingService logger, IAesEncryption aesEncryption)
        {
            _context = context;
            _logger = logger;
            _configuration = configuration;
            _aesEncryption = aesEncryption;

            key = configuration["Razorpay:Key"] ?? "";
            secret = configuration["Razorpay:Secret"] ?? "";
            _razorpayClient = new RazorpayClient(key, secret);

            string stringKey = configuration["Encryption:PaymentKey"] ?? "";
            encryptionKey = Convert.FromBase64String(stringKey);
        }

        public CreateOrderVM CreateOrder(double amount, Employee loggedInEmployee, Guid tenantId)
        {
            RazorpayClient client = new RazorpayClient(key, secret);

            var receipt = $"rec_{Guid.NewGuid()}";
            var length = receipt.Length;

            Dictionary<string, object> options = new Dictionary<string, object>
            {
                { "amount", amount * 100 }, // amount in paise
                { "currency", "INR" },
                { "receipt",  receipt},
                { "payment_capture", 1 }
            };

            Order order = client.Order.Create(options);
            var response = new CreateOrderVM
            {
                OrderId = order["id"],
                Key = key
            };
            return response;
        }

        public string GetExpectedSignature(string payload)
        {
            string expectedSignature;
            using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
            {
                var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
                expectedSignature = Convert.ToHexString(hash).ToLower();
            }

            return expectedSignature;
        }

        /// <summary>
        /// Fetch complete payment details from Razorpay including card details
        /// </summary>
        /// <param name="paymentId">Razorpay Payment ID</param>
        /// <returns>Complete payment details with card information</returns>
        public async Task<PaymentDetailsVM> GetPaymentDetails(string paymentId)
        {
            try
            {
                _logger.LogInfo("Fetching payment details from Razorpay for PaymentId: {PaymentId}", paymentId);

                // Fetch payment details from Razorpay
                Payment payment = _razorpayClient.Payment.Fetch(paymentId);

                // Extract customer name from notes or fetch from customer API
                string customerName = ExtractCustomerName(payment);

                Guid paymentDetailsId = Guid.NewGuid();

                // Map to custom model with all details
                var paymentDetails = new RazorpayPaymentDetails
                {
                    PaymentId = payment.Attributes["id"]?.ToString(),
                    OrderId = payment.Attributes["order_id"]?.ToString(),
                    Amount = Convert.ToDecimal(payment.Attributes["amount"]) / 100, // Convert from paise to rupees
                    Currency = payment.Attributes["currency"]?.ToString(),
                    Status = payment.Attributes["status"]?.ToString(),
                    Method = payment.Attributes["method"]?.ToString(),
                    Email = payment.Attributes["email"]?.ToString(),
                    Contact = payment.Attributes["contact"]?.ToString(),
                    Description = payment.Attributes["description"]?.ToString(),
                    CustomerName = customerName,
                    CreatedAt = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(payment.Attributes["created_at"])).DateTime,

                    // Card details (if payment method is card)
                    CardDetails = payment.Attributes["method"]?.ToString() == "card"
                        ? ExtractCardDetails(payment)
                        : null,

                    // Bank details (if payment method is netbanking)
                    BankDetails = payment.Attributes["method"]?.ToString() == "netbanking"
                        ? ExtractBankDetails(payment)
                        : null,

                    // UPI details (if payment method is upi)
                    UpiDetails = payment.Attributes["method"]?.ToString() == "upi"
                        ? ExtractUpiDetails(payment)
                        : null,

                    // Wallet details (if payment method is wallet)
                    WalletDetails = payment.Attributes["method"]?.ToString() == "wallet"
                        ? ExtractWalletDetails(payment)
                        : null,

                    // Additional details
                    Fee = payment.Attributes["fee"] != null
                        ? Convert.ToDecimal(payment.Attributes["fee"]) / 100
                        : 0,
                    Tax = payment.Attributes["tax"] != null
                        ? Convert.ToDecimal(payment.Attributes["tax"]) / 100
                        : 0,
                    ErrorCode = payment.Attributes["error_code"]?.ToString(),
                    ErrorDescription = payment.Attributes["error_description"]?.ToString(),
                    InternationalPayment = Convert.ToBoolean(payment.Attributes["international"] ?? false),
                    Captured = Convert.ToBoolean(payment.Attributes["captured"] ?? false)
                };

                var razorpayOrderDetails = GetOrderDetails(paymentDetails.OrderId ?? "");

                var response = new PaymentDetailsVM
                {
                    Id = paymentDetailsId,
                    RazorpayPaymentDetails = paymentDetails,
                    RazorpayOrderDetails = razorpayOrderDetails
                };

                string jsonString = JsonConvert.SerializeObject(response);

                var data = _aesEncryption.Encrypt(jsonString, encryptionKey);

                var paymentDetail = new PaymentDetail
                {
                    Id = paymentDetailsId,
                    PaymentId = paymentDetails.PaymentId ?? "",
                    OrderId = paymentDetails.OrderId ?? "",
                    Status = paymentDetails.Status ?? "",
                    Method = paymentDetails.Method ?? "",
                    CreatedAt = DateTime.UtcNow,
                    EncryptedDetails = data.ciphertext,
                    Nonce = data.nonce,
                    Tag = data.tag
                };

                _context.PaymentDetails.Add(paymentDetail);
                await _context.SaveChangesAsync();

                _logger.LogInfo("Payment details fetched successfully from Razorpay for PaymentId: {PaymentId}", paymentId);
                return response;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching payment details from Razorpay for PaymentId: {PaymentId}", paymentId);
                return new PaymentDetailsVM();
            }
        }

        /// <summary>
        /// Fetch order details from Razorpay
        /// </summary>
        /// <param name="orderId">Razorpay Order ID</param>
        /// <returns>Order details</returns>
        public RazorpayOrderDetails? GetOrderDetails(string orderId)
        {
            try
            {
                _logger.LogInfo("Fetching order details from Razorpay for OrderId: {OrderId}", orderId);

                Order order = _razorpayClient.Order.Fetch(orderId);

                var orderDetails = new RazorpayOrderDetails
                {
                    OrderId = order.Attributes["id"]?.ToString(),
                    Amount = Convert.ToDecimal(order.Attributes["amount"]) / 100,
                    Currency = order.Attributes["currency"]?.ToString(),
                    Status = order.Attributes["status"]?.ToString(),
                    Receipt = order.Attributes["receipt"]?.ToString(),
                    CreatedAt = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(order.Attributes["created_at"])).DateTime,
                    AmountPaid = order.Attributes["amount_paid"] != null
                        ? Convert.ToDecimal(order.Attributes["amount_paid"]) / 100
                        : 0,
                    AmountDue = order.Attributes["amount_due"] != null
                        ? Convert.ToDecimal(order.Attributes["amount_due"]) / 100
                        : 0,
                    Attempts = Convert.ToInt32(order.Attributes["attempts"] ?? 0)
                };

                _logger.LogInfo("Order details fetched successfully from Razorpay for OrderId: {OrderId}", orderId);
                return orderDetails;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching order details from Razorpay for OrderId: {OrderId}", orderId);
                return null;
            }
        }

        public async Task<PaymentDetailsVM> GetPaymentDetailsFromDataBase(Guid id)
        {
            var projectDetails = await _context.PaymentDetails.FirstOrDefaultAsync(pd => pd.Id == id);
            if (projectDetails == null)
            {
                return new PaymentDetailsVM();
            }
            string decrypedData = _aesEncryption.Decrypt(projectDetails.EncryptedDetails ?? new byte[32], projectDetails.Nonce ?? new byte[12], projectDetails.Tag ?? new byte[16], encryptionKey);
            // Deserialize JSON string to a Department object
            PaymentDetailsVM? vm = JsonConvert.DeserializeObject<PaymentDetailsVM>(decrypedData);
            if (vm == null)
            {
                return new PaymentDetailsVM();
            }
            else
            {
                return vm;
            }
        }

        public async Task<List<PaymentDetailsVM>> GetPaymentDetailsListFromDataBase(List<Guid> paymentDetailsIds)
        {
            var projectDetails = await _context.PaymentDetails.Where(pd => paymentDetailsIds.Contains(pd.Id)).ToListAsync();
            List<PaymentDetailsVM> response = new List<PaymentDetailsVM>();

            foreach (var projectDetail in projectDetails)
            {
                string decrypedData = _aesEncryption.Decrypt(projectDetail.EncryptedDetails ?? new byte[32], projectDetail.Nonce ?? new byte[12], projectDetail.Tag ?? new byte[16], encryptionKey);
                // Deserialize JSON string to a Department object
                PaymentDetailsVM? vm = JsonConvert.DeserializeObject<PaymentDetailsVM>(decrypedData);
                if (vm != null)
                {
                    response.Add(vm);
                }
            }

            return response;
        }

        /// <summary>
        /// Extract card details from payment
        /// </summary>
        private CardDetails? ExtractCardDetails(Payment payment)
        {
            try
            {
                var cardObj = payment.Attributes["card"];
                if (cardObj == null) return null;

                string json = JsonConvert.SerializeObject(cardObj);
                Dictionary<string, object>? cardDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

                return new CardDetails
                {
                    CardId = cardDict?["id"]?.ToString(),
                    Last4Digits = cardDict?["last4"]?.ToString(),
                    Network = cardDict?["network"]?.ToString(), // Visa, MasterCard, Amex, etc.
                    CardType = cardDict?["type"]?.ToString(), // credit, debit, prepaid
                    Issuer = cardDict?["issuer"]?.ToString(), // Bank name
                    International = Convert.ToBoolean(cardDict?["international"] ?? false),
                    Emi = Convert.ToBoolean(cardDict?["emi"] ?? false),
                    SubType = cardDict?["sub_type"]?.ToString() // consumer, business
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error extracting card details");
                return null;
            }
        }

        /// <summary>
        /// Extract bank details from payment
        /// </summary>
        private BankDetails? ExtractBankDetails(Payment payment)
        {
            try
            {
                return new BankDetails
                {
                    Bank = payment.Attributes["bank"]?.ToString(),
                    BankCode = payment.Attributes["bank_code"]?.ToString()
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error extracting bank details");
                return null;
            }
        }

        /// <summary>
        /// Extract UPI details from payment
        /// </summary>
        private UpiDetails? ExtractUpiDetails(Payment payment)
        {
            try
            {
                var vpaObj = payment.Attributes["vpa"];

                return new UpiDetails
                {
                    Vpa = vpaObj?.ToString(), // UPI ID
                    Provider = payment.Attributes["provider"]?.ToString()
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error extracting UPI details");
                return null;
            }
        }

        /// <summary>
        /// Extract wallet details from payment
        /// </summary>
        private WalletDetails? ExtractWalletDetails(Payment payment)
        {
            try
            {
                return new WalletDetails
                {
                    WalletName = payment.Attributes["wallet"]?.ToString() // paytm, phonepe, amazonpay, etc.
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error extracting wallet details");
                return null;
            }
        }

        /// <summary>
        /// Extract customer name from payment object
        /// </summary>
        private string ExtractCustomerName(Payment payment)
        {
            try
            {
                // Option 1: Get from notes (if you passed customer name during order creation)
                var notesObj = payment.Attributes["notes"];
                if (notesObj != null)
                {
                    var notesDict = notesObj as Dictionary<string, object>;
                    if (notesDict != null && notesDict.ContainsKey("customer_name"))
                    {
                        return notesDict["customer_name"]?.ToString() ?? "CustomerName";
                    }
                    if (notesDict != null && notesDict.ContainsKey("name"))
                    {
                        return notesDict["name"]?.ToString() ?? "CustomerName";
                    }
                }

                // Option 2: Get from customer ID and fetch customer details
                var customerId = payment.Attributes["customer_id"]?.ToString();
                if (!string.IsNullOrEmpty(customerId))
                {
                    try
                    {
                        Customer customer = _razorpayClient.Customer.Fetch(customerId);
                        return customer.Attributes["name"]?.ToString() ?? "CustomerName";
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error fetching customer details for CustomerId: {CustomerId}", customerId);
                    }
                }

                // Option 3: Extract from card holder name (if available)
                if (payment.Attributes["method"]?.ToString() == "card")
                {
                    var cardObj = payment.Attributes["card"];
                    if (cardObj != null)
                    {
                        string json = JsonConvert.SerializeObject(cardObj);
                        Dictionary<string, object>? dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

                        var cardName = dictionary?["name"]?.ToString();
                        if (!string.IsNullOrEmpty(cardName))
                        {
                            return cardName;
                        }
                    }
                }

                // Option 4: Use email as fallback
                return payment.Attributes["email"]?.ToString() ?? "Customer";
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error extracting customer name from payment");
                return "Customer";
            }
        }
    }
}
