package com.hepl.tunefortwo.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.http.auth.AuthScope;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.bson.Document;
import org.bson.types.Decimal128;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.MongoExpression;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationExpression;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.ArrayOperators;
import org.springframework.data.mongodb.core.aggregation.ComparisonOperators;
import org.springframework.data.mongodb.core.aggregation.MatchOperation;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.util.UriComponentsBuilder;

import com.hepl.tunefortwo.entity.MasterPayment;
import com.hepl.tunefortwo.entity.PaymentTransaction;
import com.hepl.tunefortwo.dto.PaymentOrderDto;
import com.hepl.tunefortwo.entity.Form;
import com.hepl.tunefortwo.repository.FormRepository;
import com.hepl.tunefortwo.repository.MasterPaymentRepository;
import com.hepl.tunefortwo.service.MailService;
import com.hepl.tunefortwo.service.MasterPaymentService;
import com.hepl.tunefortwo.service.PaymentService;
import com.hepl.tunefortwo.service.TemplateService;
import com.hepl.tunefortwo.utils.AppMessages;
import com.hepl.tunefortwo.utils.PaymentUtils;
import com.razorpay.Order;
import com.razorpay.Payment;
import com.razorpay.RazorpayClient;
import com.razorpay.RazorpayException;

import jakarta.mail.MessagingException;

@Service
public class MasterPaymentServiceImpl implements MasterPaymentService {
	
	private static final Logger logger = LoggerFactory.getLogger(MasterPaymentService.class);
	private final MasterPaymentRepository masterPaymentRepository;
	private final FormRepository formRepository;
	private final MailService mailService;
	private final TemplateService templateService;
	
	 @Autowired
	    private MongoTemplate mongoTemplate;
	
	@Value("${razorPayCredentials.keyId}")
    private String keyId; // = "rzp_live_WBH7rGP04GwuQK";
	
	@Value("${adminMail.payment}")
	private String paymentMailId;
    
    private String keyToEncrpytKeyId ="";

    @Value("${razorPayCredentials.keySecret}")
    private String keySecret;  //= "MRdnZEkkj7ikFdcwiJTbzoV9";
    
    private static final String BASE_URL = "https://api.razorpay.com/v1/orders/{order_id}/payments";
    
    
    public MasterPaymentServiceImpl(MasterPaymentRepository masterPaymentRepository,FormRepository formRepository,MailService mailService,TemplateService templateService) {
    	this.masterPaymentRepository = masterPaymentRepository;
    	this.formRepository = formRepository;
    	this.mailService = mailService;
    	this.templateService = templateService;
    }

//    private final RazorpayClient razorpayClient;      
   
    public Map<String , String > createOrder(double amount,String formId) throws RazorpayException, MessagingException {
    	 String savedStatus = "unsaved";
    	 RazorpayClient razorpayClient = new RazorpayClient(keyId, keySecret);
         JSONObject orderRequest = new JSONObject();
         long amountInPaisa = (long) (amount * 100);
//         long amountInPaisa = 100;
         orderRequest.put("amount", amountInPaisa);
         orderRequest.put("currency", "INR");
         orderRequest.put("receipt", "order_receipt_11");
         

         Order order = razorpayClient.Orders.create(orderRequest);
         logger.debug("Order Data from Razorpay: {}", order);
         System.out.println("OrderData"+order);
         String orderId = order.get("id");
         int dueOrderAmount = order.get("amount_due");
         int paidOrderAmount = order.get("amount_paid");
         
         String currency = order.get("currency");
         int attempts = order.get("attempts");
         Map<String , String > paymentIdMap = new HashMap<>();
         paymentIdMap.put("OrderId", orderId);
//         paymentIdMap.put("KeyId",keyId);
         
         List<MasterPayment> ordersFound = masterPaymentRepository.findByOrderId(orderId);
         if(!(ordersFound.size()>0)) {
	         Optional<Form> optionalFormDataForOrder = formRepository.findById(formId); 
	         if(!optionalFormDataForOrder.isEmpty()) {
	        	 Form formDataForOrder = optionalFormDataForOrder.get();
	        	 String userName = formDataForOrder.getName();
	        	 String email = formDataForOrder.getMail();
	        	 MasterPayment newOrder = new MasterPayment();
	             newOrder.setOrderId(orderId);
	             newOrder.setFormId(formId);
	             newOrder.setFormOrderNumber(formDataForOrder.getOrderNumber());
	             newOrder.setContactMail(formDataForOrder.getMail());
	             newOrder.setContactNumber(formDataForOrder.getPhonenumber());
	             BigDecimal paisaAmountDue = getNumberAsBigDecimal(order.get("amount_due"));
	             BigDecimal paisaAmountPaid = getNumberAsBigDecimal(order.get("amount_paid"));
//	             BigDecimal paisaAmountCharged = getNumberAsBigDecimal(amount);
	             BigDecimal amountDueInRupees = paisaAmountDue.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
	             BigDecimal amountPaidInRupees = paisaAmountPaid.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
	             
	             BigDecimal amountChargedInRupees = BigDecimal.valueOf(amountInPaisa).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
	             newOrder.setAmountPaid(amountPaidInRupees);
	             newOrder.setAmountDue(amountDueInRupees);
	             newOrder.setAmountCharged(amountChargedInRupees);
//	             newOrder.setAmountDue(BigDecimal.valueOf(dueOrderAmount));
//	             newOrder.setAmountPaid(BigDecimal.valueOf(paidOrderAmount));
	             newOrder.setCurrency(currency);
	             newOrder.setOrderAttempts(attempts);
	             newOrder.setUserName(userName);
	             newOrder.setCreatedAt(LocalDateTime.now());
	             newOrder.setOrderStatus(getStringValue(order.get("status")));
	             newOrder.setOrderReceipt(getStringValue(order.get("receipt")));
	             formDataForOrder.setPaymentOrderId(orderId);
	             formDataForOrder.setPaymentStatus(getStringValue(order.get("status")));
	             formRepository.save(formDataForOrder);
	             
	             MasterPayment createdPaymentOrder= masterPaymentRepository.save(newOrder);
	             if(createdPaymentOrder != null) {
	            	 savedStatus = "saved";
	             }
	         }
	         else {
//	        	 MasterPayment newOrder = new MasterPayment();
//	        	 
//	//        	 Form optionalForm = formRepository.findById(formId)
//	//     				.orElseThrow(()->new ResponseStatusException(HttpStatus.NOT_FOUND,
//	//     						AppMessages.SONG_NOT_FOUND+ "with ID: " + formId));
//	             newOrder.setOrderId(orderId);
//	             newOrder.setFormId(formId);
//	             newOrder.setAmountCharged(BigDecimal.valueOf(amount));
//	             newOrder.setAmountDue(BigDecimal.valueOf(dueOrderAmount));
//	             newOrder.setAmountPaid(BigDecimal.valueOf(paidOrderAmount));
//	             newOrder.setCurrency(currency);
//	             newOrder.setOrderAttempts(attempts);
//	             newOrder.setUserName(optionalForm.getName());
//	             newOrder.setCreatedAt(LocalDateTime.now());
//	             MasterPayment createdPaymentOrder= masterPaymentRepository.save(newOrder);
	        	 throw new MessagingException("Form with ID: " + formId + " does not exist.");
	         }
	         paymentIdMap.put("savedStatus", savedStatus);
         }
         else {
        	 throw new MessagingException("Order with orderId: "+orderId+" already Exists");
         }
//         return orderId;

        return paymentIdMap;
    }

    public boolean verifyPayment(String paymentId, String orderId, String signature) {
        return PaymentUtils.verifyPaymentSignature(paymentId, orderId, signature, keySecret);
    }

//	@Override
//	public void getPaymentsForAnId(String paymentId) throws RazorpayException {
//		String apiKey = "rzp_test_61o42b5BjTLUoB";
//	    String apiSecret = "aPV4FslWnQewmemFWhAZxW7I";
//
//	    // Create a custom HttpClient with Basic Authentication
//	    BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
//	    credentialsProvider.setCredentials(
//	        AuthScope.ANY,
//	        new org.apache.http.auth.UsernamePasswordCredentials(apiKey, apiSecret)
//	    );
//
//	    CloseableHttpClient httpClient = HttpClients.custom()
//	            .setDefaultCredentialsProvider(credentialsProvider)
//	            .build();
//
//	    // Create a custom RestTemplate with the HttpClient
//	    RestTemplate restTemplate = new RestTemplate();
//
//	    String urlTemplate = BASE_URL;
////	    + "/orders/{order_id}/payments";
//
//	    // Build the URL with orderId
//	    String url = UriComponentsBuilder.fromHttpUrl(urlTemplate)
//	                                  .buildAndExpand(orderId)
//	                                  .toUriString();
//
//	    // Perform the GET request
//	    try {
//	        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, String.class);
//	        String responseBody = response.getBody();
//	        // Process the response
//	        System.out.println(responseBody);
//	    } catch (Exception e) {
//	        e.printStackTrace();
//	        // Handle exceptions as needed
//	    }
//		
//	}
    @Override
	public String setPaymentsForAnOrderId(String paymentId, String paymentsignature) throws RazorpayException, JsonMappingException, JsonProcessingException, MessagingException {
    	RazorpayClient razorpay = new RazorpayClient(keyId, keySecret);
    	Payment payment = razorpay.Payments.fetch(paymentId);
    	
//        logger.debug("Payment Data from Razorpay: {}", payment);
    		System.out.println("payment Data: "+ payment);
    	 	String id = payment.get("id");
    	 	String orderId = payment.get("order_id");
    	 	logger.debug("HandlerMethod Called - paymentId: {}, orderId: {}", paymentId, orderId);
//    	    int amountPaid = payment.get("amount"); // amount is in paise, converting to rupees
    	    String paymentMethod = payment.get("method");
    	    String paymentStatus = payment.get("status");
    	    
    	    Optional<MasterPayment> existingPayment = masterPaymentRepository.findByPaymentId(paymentId);
    	    if (existingPayment.isPresent()) {
    	        return "Payment with this ID has already been processed.";
    	    }
    	    
    	    List<MasterPayment> paymentsForOrdersFound = masterPaymentRepository.findByOrderId(orderId);
    	    if(paymentsForOrdersFound.size()>0) {
    	    
    	    	 MasterPayment paymentsForOrderFound = paymentsForOrdersFound.get(0);
    	    	 Order orderDataToBeUpdated = razorpay.Orders.fetch(orderId);
    	    	 System.out.println("orderDataToBeUpdated : "+orderDataToBeUpdated);
    	    	 BigDecimal paisaAmount = getNumberAsBigDecimal(orderDataToBeUpdated.get("amount"));
    	         BigDecimal paisaAmountPaid = getNumberAsBigDecimal(orderDataToBeUpdated.get("amount_paid"));
    	         BigDecimal paisaAmountDue = getNumberAsBigDecimal(orderDataToBeUpdated.get("amount_due"));
    	         paymentsForOrderFound.setAmountCharged(paisaAmount.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));    	    	 
    	    	 paymentsForOrderFound.setAmountPaid(paisaAmountPaid.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
    	    	 paymentsForOrderFound.setAmountDue(paisaAmountDue.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
    	    	 List<LocalDateTime> updatedAtList = paymentsForOrderFound.getUpdatedAtList();
    	    	    if (updatedAtList == null) {
    	    	        updatedAtList = new ArrayList<>();
    	    	        paymentsForOrderFound.setUpdatedAtList(updatedAtList);
    	    	    }
    	    	    updatedAtList.add(LocalDateTime.now());
    	    	 
    	    	 paymentsForOrderFound.setOrderAttempts(orderDataToBeUpdated.get("attempts"));
    	    	 paymentsForOrderFound.setOrderStatus(orderDataToBeUpdated.get("status"));
    	    	    
    	    	 masterPaymentRepository.save(paymentsForOrderFound);
    	    	 logger.debug("Payment MailId {}:", paymentMailId);
    	    	 
    	    	 String formIdFound = paymentsForOrderFound.getFormId();
    	    	 Optional<Form> optionalFormDataForOrder = formRepository.findById(formIdFound);
    	    	 Form formDataForOrderForMail = optionalFormDataForOrder.get();
    	    	 mailService.sendMailByTemplate(
 		                templateService.getPaymentMessageTemplate(payment.get("status"), payment.get("method"),paisaAmountPaid.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP),formDataForOrderForMail.getName(),formDataForOrderForMail.getOrderNumber(),orderDataToBeUpdated.get("attempts"),LocalDate.now(),paisaAmount.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP),paymentId,formDataForOrderForMail.getMail(),formDataForOrderForMail.getPhonenumber()), 
 		               paymentMailId, 
 		                "Payment Details Mail"
 		            );
    	    	 
    	    	 if(paymentStatus.equals("captured")) {
    	    		 
    		         if(!optionalFormDataForOrder.isEmpty()) {
    		        	 Form formDataForOrder = optionalFormDataForOrder.get();
    		        	 if(orderDataToBeUpdated.get("status").equals("paid")) {
    	    	    		  formDataForOrder.setPaymentStatus(getStringValue(orderDataToBeUpdated.get("status")));
    	    	    	 }
    		        	 formDataForOrder.setPaymentId(paymentId);
    		        	 formRepository.save(formDataForOrder);
    	    	    }
    	    	 }
    	    	 

    	    	 
		    	    if(paymentMethod.equals("netbanking")) {
		    	    	MasterPayment netBankingData = this.paymentWithNetBanking(payment, orderId,paymentsForOrderFound,paymentsignature);
		    	    	if(netBankingData!=null) {
		    	    		return "NetBanking payment Data updated Successfully";
		    	    	}
		    	    }
		    	    if(paymentMethod.equals("card")) {
		    	    	MasterPayment cardData = this.paymentWithCard(payment, orderId, paymentsForOrderFound,paymentsignature);
		    	    	if(cardData!=null) {
		    	    		return "Card payment Data updated Successfully";
		    	    	}
		    	    	
		    	    }
		    	    if(paymentMethod.equals("upi")) {
		    	    	MasterPayment upiData =	this.paymentWithUPI(payment, orderId, paymentsForOrderFound,paymentsignature);
		    	    	if(upiData!=null) {
		    	    		return "UPI payment Data updated Successfully";
		    	    	}
		    	    }
		    	    if(paymentMethod.equals("wallet")) {
		    	    	MasterPayment upiData =	this.paymentWithWallet(payment, orderId, paymentsForOrderFound,paymentsignature);
		    	    	if(upiData!=null) {
		    	    		return "UPI payment Data updated Successfully";
		    	    	}
		    	    }
    	    }
    	    
    	    
    	    
    	return "No orders Found";
    	 
    	
    	
    }

    @Override
    public MasterPayment paymentWithNetBanking(Payment payment, String orderId, MasterPayment paymentsForOrderFound, String signature) throws JsonMappingException, JsonProcessingException {
        MasterPayment newData = new MasterPayment();

        
        List<PaymentTransaction> paymentTransactions = paymentsForOrderFound.getPayments();
        if (paymentTransactions == null) {
            paymentTransactions = new ArrayList<>();
        }

        
        PaymentTransaction paymentTransaction = new PaymentTransaction();
        paymentTransaction.setType("netbanking");
        paymentTransaction.setPaymentId(payment.get("id"));
        if (signature == null) {
            paymentTransaction.setPaymentSignature("no signature found");
        } else {
            paymentTransaction.setPaymentSignature(signature);
        }
        
        Object createdAtObject = payment.get("created_at");
        LocalDateTime createdAt = null;
        if (createdAtObject instanceof Date) {
            Date createdAtDate = (Date) createdAtObject;
            Instant instant = createdAtDate.toInstant();
            createdAt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        } else if (createdAtObject instanceof Number) {
            long currentTimeMillis = ((Number) createdAtObject).longValue();
            Instant instant = Instant.ofEpochMilli(currentTimeMillis);
            createdAt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        }
        
        paymentTransaction.setCreatedDate(createdAt);

        
//        String bank = (String) payment.get("bank");
        paymentTransaction.setBank(getStringValue(payment.get("bank")));

        
//        Object acquirerDataObject = payment.get("acquirer_data");
//        if (acquirerDataObject != null) {
//            String acquirerDataString;
//            if (acquirerDataObject instanceof String) {
//                acquirerDataString = (String) acquirerDataObject;
//            } else {
//                acquirerDataString = acquirerDataObject.toString(); 
//            }
//
//            
//            System.out.println("Raw acquirer_data: " + acquirerDataString);
//
//            ObjectMapper mapper = new ObjectMapper();
//            JsonNode jsonNode = mapper.readTree(acquirerDataString);
//
//            
//            String bankTransactionId = jsonNode.get("bank_transaction_id").asText();
//            paymentTransaction.setBankTransactionId(bankTransactionId);
//        }
//
//        
//        Object notesObj = payment.get("notes");
//        if (notesObj != null) {
//            String notesString;
//            if (notesObj instanceof String) {
//                notesString = (String) notesObj;
//            } else {
//                notesString = notesObj.toString(); 
//            }
//
//            
//            System.out.println("Raw Notes: " + notesString);
//
//            ObjectMapper mapperForNotes = new ObjectMapper();
//            JsonNode jsonNodeForNotes = mapperForNotes.readTree(notesString);
//
//            
//            String notesField = jsonNodeForNotes.get("address").asText();
//            paymentTransaction.setNotes(notesField);
//        }
        Object acquirerDataObject = payment.get("acquirer_data");
	    if (acquirerDataObject != null) {
	        ObjectMapper mapper = new ObjectMapper();
	        JsonNode jsonNode;
	        
	        if (acquirerDataObject instanceof org.json.JSONObject) {
	            String acquirerDataString = acquirerDataObject.toString();
	            jsonNode = mapper.readTree(acquirerDataString);
	        } else {
	            jsonNode = mapper.convertValue(acquirerDataObject, JsonNode.class);
	        }
	        
	        String authCode = jsonNode.has("bank_transaction_id") ? jsonNode.get("bank_transaction_id").asText() : null;
	        paymentTransaction.setBankTransactionId(authCode);
	        StringBuilder formattedAcquirerData = new StringBuilder();
            Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNode.fields();
            while (fieldsIterator.hasNext()) {
                Map.Entry<String, JsonNode> field = fieldsIterator.next();
                String key = field.getKey();
                String value = field.getValue().asText();

                
                if (value != null && !value.trim().isEmpty()) {
                    if (formattedAcquirerData.length() > 0) {
                        formattedAcquirerData.append(" | "); 
                    }
                    formattedAcquirerData.append(key).append(": ").append(value);
                }
            }

            
            paymentTransaction.setAcquirerData(formattedAcquirerData.toString());
	    }

	    
	    Object notesObj = payment.get("notes");
	    if (notesObj != null) {
	        ObjectMapper mapperForNotes = new ObjectMapper();
	        JsonNode jsonNodeForNotes;
	        
	        if (notesObj instanceof org.json.JSONObject) {
	            // Convert org.json.JSONObject to String then parse
	            String notesString = notesObj.toString();
	            jsonNodeForNotes = mapperForNotes.readTree(notesString);
	        } else {
	            jsonNodeForNotes = mapperForNotes.convertValue(notesObj, JsonNode.class);
	        }
	        
	        
	        StringBuilder formattedNotes = new StringBuilder();
	        Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNodeForNotes.fields();
	        while (fieldsIterator.hasNext()) {
	            Map.Entry<String, JsonNode> field = fieldsIterator.next();
	            String key = field.getKey();
	            String value = field.getValue().asText();
	            
	            
	            if (value != null && !value.trim().isEmpty()) {
	                if (formattedNotes.length() > 0) {
	                    formattedNotes.append(" | "); 
	                }
	                formattedNotes.append(key).append(": ").append(value);
	            }
	        }
	        paymentTransaction.setNotes(formattedNotes.toString());
	    }

        paymentTransaction.setErrorCode(getStringValue(payment.get("error_code")));
        paymentTransaction.setErrorDescription(getStringValue(payment.get("error_description")));
        paymentTransaction.setErrorSource(getStringValue(payment.get("error_source")));
        paymentTransaction.setErrorReason(getStringValue(payment.get("error_reason")));
        paymentTransaction.setErrorStep(getStringValue(payment.get("error_step")));
        BigDecimal paisaTax = getNumberAsBigDecimal(payment.get("tax"));
        BigDecimal paisaFee = getNumberAsBigDecimal(payment.get("fee"));
        paymentTransaction.setTax(paisaTax.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
        paymentTransaction.setFee(paisaFee.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
        paymentTransaction.setInvoiceId(getStringValue(payment.get("invoice_id")));
        paymentTransaction.setStatus(getStringValue(payment.get("status")));
        paymentTransaction.setAmountRefunded(getNumberAsBigDecimal(payment.get("amount_refunded")));
        BigDecimal paisaAmount = getNumberAsBigDecimal(payment.get("amount"));
	    BigDecimal amountInRupees = paisaAmount.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
	    paymentTransaction.setAmountPaid(amountInRupees);

        paymentTransaction.setIsCaptured(getBooleanValue(payment.get("captured")));
        paymentTransaction.setContactEmail(getStringValue(payment.get("email")));
        paymentTransaction.setContactNo(getStringValue(payment.get("contact")));
        paymentTransaction.setRefundStatus(getStringValue(payment.get("refund_status")));

        
        paymentTransactions.add(paymentTransaction);
        paymentsForOrderFound.setPayments(paymentTransactions);

        
        masterPaymentRepository.save(paymentsForOrderFound);
        newData = paymentsForOrderFound;

        return newData;
    }



	@Override
	public MasterPayment paymentWithUPI(Payment payment, String orderId,MasterPayment paymentsForOrderFound, String signature) throws JsonMappingException, JsonProcessingException {
		MasterPayment newData = new MasterPayment();

        
        List<PaymentTransaction> paymentTransactions = paymentsForOrderFound.getPayments();
        if (paymentTransactions == null) {
            paymentTransactions = new ArrayList<>();
        }

        
        PaymentTransaction paymentTransaction = new PaymentTransaction();
        paymentTransaction.setType("upi");
        paymentTransaction.setPaymentId(payment.get("id"));
        if (signature == null) {
            paymentTransaction.setPaymentSignature("no signature found");
        } else {
            paymentTransaction.setPaymentSignature(signature);
        }
        
        Object createdAtObject = payment.get("created_at");
        LocalDateTime createdAt = null;
        if (createdAtObject instanceof Date) {
            Date createdAtDate = (Date) createdAtObject;
            Instant instant = createdAtDate.toInstant();
            createdAt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        } else if (createdAtObject instanceof Number) {
            long currentTimeMillis = ((Number) createdAtObject).longValue();
            Instant instant = Instant.ofEpochMilli(currentTimeMillis);
            createdAt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        }
        
        paymentTransaction.setCreatedDate(createdAt);

        
//        String bank = (String) payment.get("bank");
        paymentTransaction.setBank(getStringValue(payment.get("bank")));

        
//        Object acquirerDataObject = payment.get("acquirer_data");
//        if (acquirerDataObject != null) {
//            String acquirerDataString;
//            if (acquirerDataObject instanceof String) {
//                acquirerDataString = (String) acquirerDataObject;
//            } else {
//                acquirerDataString = acquirerDataObject.toString(); 
//            }
//
//            
//            System.out.println("Raw acquirer_data: " + acquirerDataString);
//
//            ObjectMapper mapper = new ObjectMapper();
//            JsonNode jsonNode = mapper.readTree(acquirerDataString);
//
//            
//            String bankTransactionId = jsonNode.get("bank_transaction_id").asText();
//            paymentTransaction.setBankTransactionId(bankTransactionId);
//        }
//
//        
//        Object notesObj = payment.get("notes");
//        if (notesObj != null) {
//            String notesString;
//            if (notesObj instanceof String) {
//                notesString = (String) notesObj;
//            } else {
//                notesString = notesObj.toString();
//            }
//
//            
//            System.out.println("Raw Notes: " + notesString);
//
//            ObjectMapper mapperForNotes = new ObjectMapper();
//            JsonNode jsonNodeForNotes = mapperForNotes.readTree(notesString);
//
//            
//            String notesField = jsonNodeForNotes.get("address").asText();
//            paymentTransaction.setNotes(notesField);
//        }
//        Object acquirerDataObject = payment.get("acquirer_data");
//	    if (acquirerDataObject != null) {
//	        ObjectMapper mapper = new ObjectMapper();
//	        JsonNode jsonNode;
//	        
//	        if (acquirerDataObject instanceof org.json.JSONObject) {
//	            
//	            String acquirerDataString = acquirerDataObject.toString();
//	            jsonNode = mapper.readTree(acquirerDataString);
//	        } else {
//	            jsonNode = mapper.convertValue(acquirerDataObject, JsonNode.class);
//	        }
//	        
//	        String authCode = jsonNode.has("auth_code") ? jsonNode.get("auth_code").asText() : null;
//	        paymentTransaction.setBankTransactionId(authCode);
//	    }
        Object acquirerDataObject = payment.get("acquirer_data");

        if (acquirerDataObject != null) {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode jsonNode;

            if (acquirerDataObject instanceof org.json.JSONObject) {
                
                String acquirerDataString = acquirerDataObject.toString();
                jsonNode = mapper.readTree(acquirerDataString);
            } else {
                jsonNode = mapper.convertValue(acquirerDataObject, JsonNode.class);
            }
            
            String upiTransactionId = jsonNode.has("upi_transaction_id") ? jsonNode.get("upi_transaction_id").asText() : null;
            String rrnId = jsonNode.has("rrn") ? jsonNode.get("rrn").asText() : null;
	        paymentTransaction.setUpiTransactionId(upiTransactionId);
	        paymentTransaction.setRetrievalReferenceNumber(rrnId);
            
            StringBuilder formattedAcquirerData = new StringBuilder();
            Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNode.fields();
            while (fieldsIterator.hasNext()) {
                Map.Entry<String, JsonNode> field = fieldsIterator.next();
                String key = field.getKey();
                String value = field.getValue().asText();

                
                if (value != null && !value.trim().isEmpty()) {
                    if (formattedAcquirerData.length() > 0) {
                        formattedAcquirerData.append(" | "); 
                    }
                    formattedAcquirerData.append(key).append(": ").append(value);
                }
            }

            
            paymentTransaction.setAcquirerData(formattedAcquirerData.toString());
        }

	    
	    Object notesObj = payment.get("notes");
	    if (notesObj != null) {
	        ObjectMapper mapperForNotes = new ObjectMapper();
	        JsonNode jsonNodeForNotes;
	        
	        if (notesObj instanceof org.json.JSONObject) {
	            
	            String notesString = notesObj.toString();
	            jsonNodeForNotes = mapperForNotes.readTree(notesString);
	        } else {
	            jsonNodeForNotes = mapperForNotes.convertValue(notesObj, JsonNode.class);
	        }
	        
	        
	        StringBuilder formattedNotes = new StringBuilder();
	        Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNodeForNotes.fields();
	        while (fieldsIterator.hasNext()) {
	            Map.Entry<String, JsonNode> field = fieldsIterator.next();
	            String key = field.getKey();
	            String value = field.getValue().asText();
	            
	            // Format key-value pair as "key: value"
	            if (value != null && !value.trim().isEmpty()) {
	                if (formattedNotes.length() > 0) {
	                    formattedNotes.append(" | "); 
	                }
	                formattedNotes.append(key).append(": ").append(value);
	            }
	        }
	        paymentTransaction.setNotes(formattedNotes.toString());
	    }
	    
	    Object upiObject = payment.get("upi");

	    if (upiObject != null) {
	        ObjectMapper mapper = new ObjectMapper();
	        JsonNode upiNode;

	        if (upiObject instanceof org.json.JSONObject) {
	            String upiString = upiObject.toString();
	            upiNode = mapper.readTree(upiString);
	        } else {
	            upiNode = mapper.convertValue(upiObject, JsonNode.class);
	        }

	        
	        String vpa = upiNode.get("vpa") != null ? upiNode.get("vpa").asText() : null;

	        
	        if (vpa != null) {
	            paymentTransaction.setUpiVpa(vpa); 
	        }
	    }

	    paymentTransaction.setErrorCode(getStringValue(payment.get("error_code")));
        paymentTransaction.setErrorDescription(getStringValue(payment.get("error_description")));
        paymentTransaction.setErrorSource(getStringValue(payment.get("error_source")));
        paymentTransaction.setErrorReason(getStringValue(payment.get("error_reason")));
        paymentTransaction.setErrorStep(getStringValue(payment.get("error_step")));
        BigDecimal paisaTax = getNumberAsBigDecimal(payment.get("tax"));
        BigDecimal paisaFee = getNumberAsBigDecimal(payment.get("fee"));
        paymentTransaction.setTax(paisaTax.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
        paymentTransaction.setFee(paisaFee.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
        paymentTransaction.setInvoiceId(getStringValue(payment.get("invoice_id")));
        paymentTransaction.setStatus(getStringValue(payment.get("status")));
        paymentTransaction.setAmountRefunded(getNumberAsBigDecimal(payment.get("amount_refunded")));
        BigDecimal paisaAmount = getNumberAsBigDecimal(payment.get("amount"));
	    BigDecimal amountInRupees = paisaAmount.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
	    paymentTransaction.setAmountPaid(amountInRupees);

        paymentTransaction.setIsCaptured(getBooleanValue(payment.get("captured")));
        paymentTransaction.setContactEmail(getStringValue(payment.get("email")));
        paymentTransaction.setContactNo(getStringValue(payment.get("contact")));
        paymentTransaction.setRefundStatus(getStringValue(payment.get("refund_status")));

        
        paymentTransactions.add(paymentTransaction);
        paymentsForOrderFound.setPayments(paymentTransactions);

        
        masterPaymentRepository.save(paymentsForOrderFound);
        newData = paymentsForOrderFound;

        return newData;
	}

	@Override
	public MasterPayment paymentWithCard(Payment payment, String orderId, MasterPayment paymentsForOrderFound, String signature) 
	        throws JsonMappingException, JsonProcessingException {
	    MasterPayment newDataForCardPayment = new MasterPayment();

	    
	    List<PaymentTransaction> paymentTransactions = paymentsForOrderFound.getPayments();
	    if (paymentTransactions == null) {
	        paymentTransactions = new ArrayList<>();
	    }

	    
	    PaymentTransaction paymentTransaction = new PaymentTransaction();
	    paymentTransaction.setType("card");
	    paymentTransaction.setPaymentId(payment.get("id"));
	    if (signature == null) {
	        paymentTransaction.setPaymentSignature("no signature found");
	    } else {
	        paymentTransaction.setPaymentSignature(signature);
	    }
	    
	    Object createdAtObject = payment.get("created_at");
	    LocalDateTime createdAt = null;
	    if (createdAtObject instanceof Number) {
	        long createdAtSeconds = ((Number) createdAtObject).longValue();
	        Instant instant = Instant.ofEpochSecond(createdAtSeconds);
	        createdAt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
	    } else if (createdAtObject instanceof Date) {
	        Date createdAtDate = (Date) createdAtObject;
	        Instant instant = createdAtDate.toInstant();
	        createdAt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
	    }
	    paymentTransaction.setCreatedDate(createdAt);

	    
//	    String bank = (String) payment.get("bank");
	    paymentTransaction.setBank(getStringValue(payment.get("bank")));

	    
	    Object acquirerDataObject = payment.get("acquirer_data");
	    if (acquirerDataObject != null) {
	        ObjectMapper mapper = new ObjectMapper();
	        JsonNode jsonNode;
	        
	        if (acquirerDataObject instanceof org.json.JSONObject) {
	            String acquirerDataString = acquirerDataObject.toString();
	            jsonNode = mapper.readTree(acquirerDataString);
	        } else {
	            jsonNode = mapper.convertValue(acquirerDataObject, JsonNode.class);
	        }
	        
	        String authCode = jsonNode.has("auth_code") ? jsonNode.get("auth_code").asText() : null;
	        paymentTransaction.setAuthCode(authCode);	
	        StringBuilder formattedAcquirerData = new StringBuilder();
            Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNode.fields();
            while (fieldsIterator.hasNext()) {
                Map.Entry<String, JsonNode> field = fieldsIterator.next();
                String key = field.getKey();
                String value = field.getValue().asText();

                
                if (value != null && !value.trim().isEmpty()) {
                    if (formattedAcquirerData.length() > 0) {
                        formattedAcquirerData.append(" | "); 
                    }
                    formattedAcquirerData.append(key).append(": ").append(value);
                }
            }

            
            paymentTransaction.setAcquirerData(formattedAcquirerData.toString());
	        }
	    Object cardDataObject = payment.get("card");
	    if (cardDataObject != null) {
	        ObjectMapper mapper = new ObjectMapper();
	        JsonNode jsonNode;
	        
	        if (cardDataObject instanceof org.json.JSONObject) {
	            String cardDataString = cardDataObject.toString();
	            jsonNode = mapper.readTree(cardDataString);
	        } else {
	            jsonNode = mapper.convertValue(cardDataObject, JsonNode.class);
	        }
	        
	        boolean emi = jsonNode.has("emi") ? jsonNode.get("emi").asBoolean() : false;
	        boolean international = jsonNode.has("international") ? jsonNode.get("international").asBoolean() : false;
	        paymentTransaction.setEmiCard(emi);
	        paymentTransaction.setInternationalCard(international);
	        String last4 = jsonNode.has("last4") ? jsonNode.get("last4").asText() : null;
	        paymentTransaction.setLastFourDigitsOfCard(last4);
	        String subType = jsonNode.has("sub_type") ? jsonNode.get("sub_type").asText() : null;
	        paymentTransaction.setCardSubType(subType);
	        String nameOnCard = jsonNode.has("name") ? jsonNode.get("name").asText() : null;
	        paymentTransaction.setCardName(nameOnCard);
	        String cardId = jsonNode.has("id") ? jsonNode.get("id").asText() : null;
	        paymentTransaction.setCardId(cardId);
	        String tokenIin = jsonNode.has("token_iin") ? jsonNode.get("token_iin").asText() : null;
	        paymentTransaction.setCartTokenIIn(tokenIin);
	        String cardType = jsonNode.has("type") ? jsonNode.get("type").asText() : null;
	        paymentTransaction.setCardType(cardType);
	        String cardEntity = jsonNode.has("entity") ? jsonNode.get("entity").asText() : null;
	        paymentTransaction.setCardEntity(cardEntity);
	        String cardIssuer = jsonNode.has("issuer") ? jsonNode.get("issuer").asText() : null;
	        paymentTransaction.setCardIssuer(cardIssuer);
	        String cardNetwork = jsonNode.has("network") ? jsonNode.get("network").asText() : null;
	        paymentTransaction.setCardNetwork(cardNetwork);
	        
	        
	        StringBuilder formattedCardData = new StringBuilder();
            Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNode.fields();
            while (fieldsIterator.hasNext()) {
                Map.Entry<String, JsonNode> field = fieldsIterator.next();
                String key = field.getKey();
                String value = field.getValue().asText();

                
                if (value != null && !value.trim().isEmpty()) {
                    if (formattedCardData.length() > 0) {
                    	formattedCardData.append(" | "); 
                    }
                    formattedCardData.append(key).append(": ").append(value);
                }
            }

            
            paymentTransaction.setCardData(formattedCardData.toString());
	        }

	    
	    Object notesObj = payment.get("notes");
	    if (notesObj != null) {
	        ObjectMapper mapperForNotes = new ObjectMapper();
	        JsonNode jsonNodeForNotes;
	        
	        if (notesObj instanceof org.json.JSONObject) {
	            String notesString = notesObj.toString();
	            jsonNodeForNotes = mapperForNotes.readTree(notesString);
	        } else {
	            jsonNodeForNotes = mapperForNotes.convertValue(notesObj, JsonNode.class);
	        }
	        
	        
	        StringBuilder formattedNotes = new StringBuilder();
	        Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNodeForNotes.fields();
	        while (fieldsIterator.hasNext()) {
	            Map.Entry<String, JsonNode> field = fieldsIterator.next();
	            String key = field.getKey();
	            String value = field.getValue().asText();
	            
	            
	            if (value != null && !value.trim().isEmpty()) {
	                if (formattedNotes.length() > 0) {
	                    formattedNotes.append(" | "); 
	                }
	                formattedNotes.append(key).append(": ").append(value);
	            }
	        }
	        paymentTransaction.setNotes(formattedNotes.toString());
	    }


	    paymentTransaction.setErrorCode(getStringValue(payment.get("error_code")));
        paymentTransaction.setErrorDescription(getStringValue(payment.get("error_description")));
        paymentTransaction.setErrorSource(getStringValue(payment.get("error_source")));
        paymentTransaction.setErrorReason(getStringValue(payment.get("error_reason")));
        paymentTransaction.setErrorStep(getStringValue(payment.get("error_step")));
        BigDecimal paisaTax = getNumberAsBigDecimal(payment.get("tax"));
        BigDecimal paisaFee = getNumberAsBigDecimal(payment.get("fee"));
        paymentTransaction.setTax(paisaTax.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
        paymentTransaction.setFee(paisaFee.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
	    BigDecimal paisaAmount = getNumberAsBigDecimal(payment.get("amount"));
	    BigDecimal amountInRupees = paisaAmount.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
	    paymentTransaction.setAmountPaid(amountInRupees);

	    paymentTransaction.setInvoiceId(getStringValue(payment.get("invoice_id")));
	    paymentTransaction.setStatus(getStringValue(payment.get("status")));
	    paymentTransaction.setAmountRefunded(getNumberAsBigDecimal(payment.get("amount_refunded")));
	    paymentTransaction.setIsCaptured(getBooleanValue(payment.get("captured")));
	    paymentTransaction.setContactEmail(getStringValue(payment.get("email")));
	    paymentTransaction.setContactNo(getStringValue(payment.get("contact")));
	    paymentTransaction.setRefundStatus(getStringValue(payment.get("refund_status")));

	    
	    paymentTransactions.add(paymentTransaction);
	    paymentsForOrderFound.setPayments(paymentTransactions);

	    
	    masterPaymentRepository.save(paymentsForOrderFound);
	    newDataForCardPayment = paymentsForOrderFound;

	    return newDataForCardPayment;
	}
	
	@Override
	public MasterPayment paymentWithWallet(Payment payment, String orderId, MasterPayment paymentsForOrderFound,
			String signature) throws JsonMappingException, JsonProcessingException {
		 MasterPayment newDataForWalletPayment = new MasterPayment();

		    
		    List<PaymentTransaction> paymentTransactions = paymentsForOrderFound.getPayments();
		    if (paymentTransactions == null) {
		        paymentTransactions = new ArrayList<>();
		    }

		    
		    PaymentTransaction paymentTransaction = new PaymentTransaction();
		    paymentTransaction.setType("wallet");
		    paymentTransaction.setPaymentId(payment.get("id"));
		    if (signature == null) {
		        paymentTransaction.setPaymentSignature("no signature found");
		    } else {
		        paymentTransaction.setPaymentSignature(signature);
		    }
		    
		    Object createdAtObject = payment.get("created_at");
		    LocalDateTime createdAt = null;
		    if (createdAtObject instanceof Number) {
		        long createdAtSeconds = ((Number) createdAtObject).longValue();
		        Instant instant = Instant.ofEpochSecond(createdAtSeconds);
		        createdAt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
		    } else if (createdAtObject instanceof Date) {
		        Date createdAtDate = (Date) createdAtObject;
		        Instant instant = createdAtDate.toInstant();
		        createdAt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
		    }
		    paymentTransaction.setCreatedDate(createdAt);

		    
//		    String bank = (String) payment.get("bank");
		    paymentTransaction.setBank(getStringValue(payment.get("bank")));

		    
		    Object acquirerDataObject = payment.get("acquirer_data");
		    if (acquirerDataObject != null) {
		        ObjectMapper mapper = new ObjectMapper();
		        JsonNode jsonNode;
		        
		        if (acquirerDataObject instanceof org.json.JSONObject) {
		            String acquirerDataString = acquirerDataObject.toString();
		            jsonNode = mapper.readTree(acquirerDataString);
		        } else {
		            jsonNode = mapper.convertValue(acquirerDataObject, JsonNode.class);
		        }
		        
		        String authCode = jsonNode.has("auth_code") ? jsonNode.get("auth_code").asText() : null;
		        paymentTransaction.setAuthCode(authCode);	
		        StringBuilder formattedAcquirerData = new StringBuilder();
	            Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNode.fields();
	            while (fieldsIterator.hasNext()) {
	                Map.Entry<String, JsonNode> field = fieldsIterator.next();
	                String key = field.getKey();
	                String value = field.getValue().asText();

	                
	                if (value != null && !value.trim().isEmpty()) {
	                    if (formattedAcquirerData.length() > 0) {
	                        formattedAcquirerData.append(" | "); 
	                    }
	                    formattedAcquirerData.append(key).append(": ").append(value);
	                }
	            }

	            
	            paymentTransaction.setAcquirerData(formattedAcquirerData.toString());
		        }
		    Object cardDataObject = payment.get("wallet");
		    if (cardDataObject != null) {
		        ObjectMapper mapper = new ObjectMapper();
		        JsonNode jsonNode;
		        
		        if (cardDataObject instanceof org.json.JSONObject) {
		            String cardDataString = cardDataObject.toString();
		            jsonNode = mapper.readTree(cardDataString);
		        } else {
		            jsonNode = mapper.convertValue(cardDataObject, JsonNode.class);
		        }
		        
		        boolean emi = jsonNode.has("emi") ? jsonNode.get("emi").asBoolean() : false;
		        boolean international = jsonNode.has("international") ? jsonNode.get("international").asBoolean() : false;
		        paymentTransaction.setEmiCard(emi);
		        paymentTransaction.setInternationalCard(international);
		        String last4 = jsonNode.has("last4") ? jsonNode.get("last4").asText() : null;
		        paymentTransaction.setLastFourDigitsOfCard(last4);
		        String subType = jsonNode.has("sub_type") ? jsonNode.get("sub_type").asText() : null;
		        paymentTransaction.setCardSubType(subType);
		        String nameOnCard = jsonNode.has("name") ? jsonNode.get("name").asText() : null;
		        paymentTransaction.setCardName(nameOnCard);
		        String cardId = jsonNode.has("id") ? jsonNode.get("id").asText() : null;
		        paymentTransaction.setCardId(cardId);
		        String tokenIin = jsonNode.has("token_iin") ? jsonNode.get("token_iin").asText() : null;
		        paymentTransaction.setCartTokenIIn(tokenIin);
		        String cardType = jsonNode.has("type") ? jsonNode.get("type").asText() : null;
		        paymentTransaction.setCardType(cardType);
		        String cardEntity = jsonNode.has("entity") ? jsonNode.get("entity").asText() : null;
		        paymentTransaction.setCardEntity(cardEntity);
		        String cardIssuer = jsonNode.has("issuer") ? jsonNode.get("issuer").asText() : null;
		        paymentTransaction.setCardIssuer(cardIssuer);
		        String cardNetwork = jsonNode.has("network") ? jsonNode.get("network").asText() : null;
		        paymentTransaction.setCardNetwork(cardNetwork);
		        
		        
		        StringBuilder formattedCardData = new StringBuilder();
	            Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNode.fields();
	            while (fieldsIterator.hasNext()) {
	                Map.Entry<String, JsonNode> field = fieldsIterator.next();
	                String key = field.getKey();
	                String value = field.getValue().asText();

	                
	                if (value != null && !value.trim().isEmpty()) {
	                    if (formattedCardData.length() > 0) {
	                    	formattedCardData.append(" | "); 
	                    }
	                    formattedCardData.append(key).append(": ").append(value);
	                }
	            }

	            
	            paymentTransaction.setCardData(formattedCardData.toString());
		        }

		    
		    Object notesObj = payment.get("notes");
		    if (notesObj != null) {
		        ObjectMapper mapperForNotes = new ObjectMapper();
		        JsonNode jsonNodeForNotes;
		        
		        if (notesObj instanceof org.json.JSONObject) {
		            String notesString = notesObj.toString();
		            jsonNodeForNotes = mapperForNotes.readTree(notesString);
		        } else {
		            jsonNodeForNotes = mapperForNotes.convertValue(notesObj, JsonNode.class);
		        }
		        
		        
		        StringBuilder formattedNotes = new StringBuilder();
		        Iterator<Map.Entry<String, JsonNode>> fieldsIterator = jsonNodeForNotes.fields();
		        while (fieldsIterator.hasNext()) {
		            Map.Entry<String, JsonNode> field = fieldsIterator.next();
		            String key = field.getKey();
		            String value = field.getValue().asText();
		            
		            
		            if (value != null && !value.trim().isEmpty()) {
		                if (formattedNotes.length() > 0) {
		                    formattedNotes.append(" | "); 
		                }
		                formattedNotes.append(key).append(": ").append(value);
		            }
		        }
		        paymentTransaction.setNotes(formattedNotes.toString());
		    }


		    paymentTransaction.setErrorCode(getStringValue(payment.get("error_code")));
	        paymentTransaction.setErrorDescription(getStringValue(payment.get("error_description")));
	        paymentTransaction.setErrorSource(getStringValue(payment.get("error_source")));
	        paymentTransaction.setErrorReason(getStringValue(payment.get("error_reason")));
	        paymentTransaction.setErrorStep(getStringValue(payment.get("error_step")));
	        BigDecimal paisaTax = getNumberAsBigDecimal(payment.get("tax"));
	        BigDecimal paisaFee = getNumberAsBigDecimal(payment.get("fee"));
	        paymentTransaction.setTax(paisaTax.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
	        paymentTransaction.setFee(paisaFee.divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP));
		    BigDecimal paisaAmount = getNumberAsBigDecimal(payment.get("amount"));
		    BigDecimal amountInRupees = paisaAmount.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
		    paymentTransaction.setAmountPaid(amountInRupees);

		    paymentTransaction.setInvoiceId(getStringValue(payment.get("invoice_id")));
		    paymentTransaction.setStatus(getStringValue(payment.get("status")));
		    paymentTransaction.setAmountRefunded(getNumberAsBigDecimal(payment.get("amount_refunded")));
		    paymentTransaction.setIsCaptured(getBooleanValue(payment.get("captured")));
		    paymentTransaction.setContactEmail(getStringValue(payment.get("email")));
		    paymentTransaction.setContactNo(getStringValue(payment.get("contact")));
		    paymentTransaction.setRefundStatus(getStringValue(payment.get("refund_status")));

		    
		    paymentTransactions.add(paymentTransaction);
		    paymentsForOrderFound.setPayments(paymentTransactions);

		    
		    masterPaymentRepository.save(paymentsForOrderFound);
		    newDataForWalletPayment = paymentsForOrderFound;

		    return newDataForWalletPayment;
	}


	@Override
	public void updateCommonFields(MasterPayment paymentData, Payment payment) {
//		 	paymentData.setPaymentMethod(payment.get("method"));
//	        paymentData.setPaymentId(payment.get("id"));
//	        paymentData.setStatus(payment.get("status"));
//	        paymentData.setInvoiceId(payment.get("invoice_id").toString());
//	        paymentData.setAmountRefunded(BigDecimal.valueOf((Integer) payment.get("amount_refunded")));
//	        paymentData.setRefundStatus(payment.get("refund_status").toString());
//	        paymentData.setIsCaptured(payment.get("captured"));
//	        paymentData.setContactEmail(payment.get("email").toString());
//	        paymentData.setContactNo(payment.get("contact"));
//	        paymentData.setFee(payment.get("fee").toString());
//	        paymentData.setTax(payment.get("tax").toString());
		
	}
	
	private BigDecimal getNumberAsBigDecimal(Object value) {
	    if (value instanceof Decimal128) {
	        return ((Decimal128) value).bigDecimalValue();
	    } else if (value instanceof Number) {
	        return new BigDecimal(((Number) value).toString());
	    }
	    return BigDecimal.ZERO;
	}
	
	private boolean isJSONObjectNull(Object obj) {
	    return obj == null || obj.getClass().getName().equals("org.json.JSONObject$Null");
	}
	
	private String getStringValue(Object value) {
	    if (isJSONObjectNull(value)) {
	        return null;
	    }
	    return value.toString();
	}
	

	private Boolean getBooleanValue(Object value) {
	    if (value instanceof Boolean) {
	        return (Boolean) value;
	    }
	    return Boolean.FALSE; 
	}

	@Override
	public List<PaymentTransaction> getPaymentsForOrderId(String orderId) {
	    List<PaymentTransaction> allPaymentsForAnOrder = new ArrayList<>();
	    List<MasterPayment> paymentsForOrdersFound = masterPaymentRepository.findByOrderId(orderId);

	    if (paymentsForOrdersFound.isEmpty()) {
	        throw new IllegalArgumentException("No payments found for order ID: " + orderId);
	    }
	    
	    MasterPayment order = paymentsForOrdersFound.get(0);
	    allPaymentsForAnOrder = order.getPayments();
	    
	    if (allPaymentsForAnOrder == null || allPaymentsForAnOrder.isEmpty()) {
	        throw new IllegalArgumentException("No payment transactions found for order ID: " + orderId);
	    }

	    logger.debug("paymentsForOrder results: {}", allPaymentsForAnOrder);
	    return allPaymentsForAnOrder;
	}


	@Override
	public List<MasterPayment> getPaymentsByUserName(String userName) {
		return masterPaymentRepository.findByUserNamePartialMatch(userName);
	}

	@Override
	public List<MasterPayment> getPaymentsByFormId(String formId) {
		return masterPaymentRepository.findByFormIdPartialMatch(formId);
	}

	@Override
	public List<MasterPayment> getPaymentsWithStatusCapturedOrFailed() {
		return masterPaymentRepository.findByPaymentStatusCapturedOrFailed();
	}

	@Override
	public Map<String, Object> getPaymentsWithBYStatus(String status) {
	    Aggregation aggregation = Aggregation.newAggregation(
	        Aggregation.match(Criteria.where("orderStatus").is(status)),
	        Aggregation.project("orderId", "formId", "userName", "amountCharged", "amountDue", "amountPaid",
	                             "currency", "orderAttempts", "createdAt", "formOrderNumber", 
	                             "orderStatus", "orderReceipt", "payments", "updatedAtList"),
	        Aggregation.group("_id")
	            .first("orderId").as("orderId")
	            .first("formId").as("formId")
	            .first("userName").as("userName")
	            .first("amountCharged").as("amountCharged")
	            .first("amountDue").as("amountDue")
	            .first("amountPaid").as("amountPaid")
	            .first("currency").as("currency")
	            .first("orderAttempts").as("orderAttempts")
	            .first("createdAt").as("createdAt")
	            .first("formOrderNumber").as("formOrderNumber")
	            .first("orderStatus").as("orderStatus")
	            .first("orderReceipt").as("orderReceipt")
	            .first("payments").as("payments")
	            .first("updatedAtList").as("updatedAtList")
	            .count().as("countOfMatchingPayments")
	    );

	   
	    AggregationResults<MasterPayment> results = mongoTemplate.aggregate(aggregation, "masterpayment", MasterPayment.class);

	    
	    List<MasterPayment> orderResultsForStatus = results.getMappedResults();
	    int orderResultsForStatusCount = orderResultsForStatus.size();

	    
	    Map<String, Object> pagedOrdersWithCount = new HashMap<>();
	    pagedOrdersWithCount.put("OrdersCount", orderResultsForStatusCount);
	    pagedOrdersWithCount.put("OrderData", orderResultsForStatus);

	    return pagedOrdersWithCount;
	}

	@Override
	public Map<String, Object> getAllOrders(Pageable pageable,String status,String searchQuery) {
	   
		
		if (searchQuery != null && !searchQuery.isEmpty()) {
			 String regexSearchTerm = ".*" + searchQuery + ".*";  

			    
			    Criteria criteria = new Criteria().orOperator(
			        Criteria.where("payments.paymentId").regex(regexSearchTerm, "i"),
			        Criteria.where("userName").regex(regexSearchTerm, "i"),
			        Criteria.where("orderId").regex(regexSearchTerm, "i"),
			        Criteria.where("payments.contactEmail").regex(regexSearchTerm, "i")
			    );

			    Aggregation aggregation = Aggregation.newAggregation(
			        Aggregation.match(criteria),  
			        Aggregation.unwind("payments", "preserveNullAndEmptyArrays"),
			        Aggregation.sort(Sort.by(Sort.Direction.fromString(pageable.getSort().iterator().next().getDirection().name()), pageable.getSort().iterator().next().getProperty())),
			        Aggregation.group("_id") 
			            .first("orderId").as("orderId")
			            .first("formId").as("formId")
			            .first("userName").as("userName")
			            .first("amountCharged").as("amountCharged")
			            .first("amountDue").as("amountDue")
			            .first("amountPaid").as("amountPaid")
			            .first("paymentMethod").as("paymentMethod")
			            .first("currency").as("currency")
			            .first("orderStatus").as("orderStatus")
			            .first("contactMail").as("contactMail")
			            .first("contactNumber").as("contactNumber")
			            .first("orderAttempts").as("orderAttempts")
			            .first("createdAt").as("createdAt")
			            .first("status").as("status")
			            .first("formOrderNumber").as("formOrderNumber")
			            .first("totalAmountPaid").as("totalAmountPaid")
			            .first("tokenId").as("tokenId")
			            .first("updatedAtList").as("updatedAtList")
			            .push("payments").as("payments"),
			        Aggregation.project()
			            .and("orderId").as("orderId")
			            .and("formId").as("formId")
			            .and("userName").as("userName")
			            .and("amountCharged").as("amountCharged")
			            .and("amountDue").as("amountDue")
			            .and("amountPaid").as("amountPaid")
			            .and("paymentMethod").as("paymentMethod")
			            .and("orderStatus").as("orderStatus")
			            .and("contactMail").as("contactMail")
			            .and("contactNumber").as("contactNumber")
			            .and("currency").as("currency")
			            .and("orderAttempts").as("orderAttempts")
			            .and("createdAt").as("createdAt")
			            .and("status").as("status")
			            .and("formOrderNumber").as("formOrderNumber")
			            .and("totalAmountPaid").as("totalAmountPaid")
			            .and("tokenId").as("tokenId")
			            .and("payments").as("payments") 
			            .and("updatedAtList").as("updatedAtList")
			    );

			    AggregationResults<MasterPayment> results = mongoTemplate.aggregate(aggregation, "masterpayment", MasterPayment.class);

			    List<MasterPayment> masterPayments = results.getMappedResults();
			    logger.debug("Full Aggregation Results: {}", masterPayments);

			    if (masterPayments.isEmpty()) {
			        logger.debug("No results found for the query: {}", searchQuery);
			    }
			    
			    long orderCount = masterPayments.size();
			    logger.debug("Orders count Found for pagination: {}", orderCount);
			    
			    int start = (int) pageable.getOffset();
			    int end = Math.min((start + pageable.getPageSize()), masterPayments.size());
			    List<MasterPayment> pagedOrders = masterPayments.subList(start, end);


			    // Prepare response
			    Map<String, Object> pagedOrdersWithCount = new HashMap<>();
			    pagedOrdersWithCount.put("OrdersCount", orderCount);
			    pagedOrdersWithCount.put("OrderData", pagedOrders);

			    return pagedOrdersWithCount;
			    
			    
			
		}
		if(status.equals("all")) {
			 Page<MasterPayment> pagedOrders = masterPaymentRepository.findAll(pageable);

			    
			    long orderCount = pagedOrders.getTotalElements();
			    logger.debug("Orders count Found for pagination: {}", orderCount);

			    // Prepare response
			    Map<String, Object> pagedOrdersWithCount = new HashMap<>();
			    pagedOrdersWithCount.put("OrdersCount", orderCount);
			    pagedOrdersWithCount.put("OrderData", pagedOrders.getContent());

			    return pagedOrdersWithCount;
		}
		
		if (status != null && !status.isEmpty()&& status != "all") {
			 Aggregation aggregation = Aggregation.newAggregation(
				        Aggregation.match(Criteria.where("orderStatus").is(status)),
				        Aggregation.project("orderId", "formId", "userName", "amountCharged", "amountDue", "amountPaid",
				                             "currency", "orderAttempts", "createdAt", "formOrderNumber","contactMail","contactNumber", 
				                             "orderStatus", "orderReceipt", "payments", "updatedAtList"),
				        Aggregation.group("_id")
				            .first("orderId").as("orderId")
				            .first("formId").as("formId")
				            .first("userName").as("userName")
				            .first("amountCharged").as("amountCharged")
				            .first("amountDue").as("amountDue")
				            .first("amountPaid").as("amountPaid")
				            .first("currency").as("currency")
				            .first("orderAttempts").as("orderAttempts")
				            .first("createdAt").as("createdAt")
				            .first("formOrderNumber").as("formOrderNumber")
				            .first("orderStatus").as("orderStatus")
				            .first("orderReceipt").as("orderReceipt")
				            .first("payments").as("payments")
				            .first("updatedAtList").as("updatedAtList")
				            .first("orderStatus").as("orderStatus")
				            .first("contactMail").as("contactMail")
				            .first("contactNumber").as("contactNumber")
				            .first("orderAttempts").as("orderAttempts")
				            .first("createdAt").as("createdAt")
				            .count().as("countOfMatchingPayments"),
				            Aggregation.sort(Sort.by(Sort.Direction.fromString(pageable.getSort().iterator().next().getDirection().name()), pageable.getSort().iterator().next().getProperty()))
				    );

				   
				    AggregationResults<MasterPayment> results = mongoTemplate.aggregate(aggregation, "masterpayment", MasterPayment.class);

				    
				    List<MasterPayment> orderResultsForStatus = results.getMappedResults();
				    int orderResultsForStatusCount = orderResultsForStatus.size();
				    
				    int start = (int) pageable.getOffset();
				    int end = Math.min((start + pageable.getPageSize()), orderResultsForStatus.size());
				    List<MasterPayment> pagedOrders = orderResultsForStatus.subList(start, end);

				    
				    Map<String, Object> pagedOrdersWithCount = new HashMap<>();
				    pagedOrdersWithCount.put("OrdersCount", orderResultsForStatusCount);
				    pagedOrdersWithCount.put("OrderData", pagedOrders);

				    return pagedOrdersWithCount;
			
		}
		
		
		
		
	    Page<MasterPayment> pagedOrders = masterPaymentRepository.findAll(pageable);

	    
	    long orderCount = pagedOrders.getTotalElements();
	    logger.debug("Orders count Found for pagination: {}", orderCount);

	    // Prepare response
	    Map<String, Object> pagedOrdersWithCount = new HashMap<>();
	    pagedOrdersWithCount.put("OrdersCount", orderCount);
	    pagedOrdersWithCount.put("OrderData", pagedOrders.getContent());

	    return pagedOrdersWithCount;
	}




	@Override
	public PaymentTransaction findPaymentById(String paymentId) {
		Optional<MasterPayment> masterPayment = masterPaymentRepository.findByPaymentId(paymentId);
        if (masterPayment.isPresent()) {
            
            return masterPayment.get().getPayments().stream()
                .filter(payment -> paymentId.equals(payment.getPaymentId()))
                .findFirst()
                .orElse(null);
        } else {
            return null; 
        }
	}

	@Override
	public List<PaymentTransaction> searchPaymentsByPaymentId(String query) {
	    String regexSearchTerm = ".*" + query + ".*";  

        Aggregation aggregation = Aggregation.newAggregation(
            Aggregation.unwind("payments"), 
            Aggregation.match(Criteria.where("payments.paymentId").regex(regexSearchTerm, "i")), 
            Aggregation.project("payments")  
                .and("payments.paymentId").as("paymentId")
                .and("payments.status").as("status")
                .and("payments.type").as("type")
                .and("payments.invoiceId").as("invoiceId")
                .and("payments.amountRefunded").as("amountRefunded")
                .and("payments.amountPaid").as("amountPaid")
                .and("payments.refundStatus").as("refundStatus")
                .and("payments.isCaptured").as("isCaptured")
                .and("payments.contactEmail").as("contactEmail")
                .and("payments.contactNo").as("contactNo")
                .and("payments.fee").as("fee")
                .and("payments.tax").as("tax")
                .and("payments.bankTransactionId").as("bankTransactionId")
                .and("payments.createdDate").as("createdDate")
                .and("payments.notes").as("notes")
                .and("payments.paymentSignature").as("paymentSignature")
                .and("payments.bank").as("bank")
                .and("payments.vpa").as("vpa")
                .and("payments.cardId").as("cardId")
                .and("payments.customerId").as("customerId")
                .and("payments.tokenId").as("tokenId")
                .and("payments.acquirerData").as("acquirerData")
                .and("payments.upiVpa").as("upiVpa")
                .and("payments.errorCode").as("errorCode")
                .and("payments.errorDescription").as("errorDescription")
                .and("payments.errorSource").as("errorSource")
                .and("payments.errorStep").as("errorStep")
                .and("payments.errorReason").as("errorReason")
        );

        
        AggregationResults<PaymentTransaction> results = mongoTemplate.aggregate(aggregation, "masterpayment", PaymentTransaction.class);

        
        List<PaymentTransaction> paymentTransactions = results.getMappedResults();
        logger.debug("Aggregation results: {}", paymentTransactions);
        return paymentTransactions;
    
	    
	}

	@Override
	public List<PaymentTransaction> genericSearchPayments(String query) {
		String regexSearchTerm = ".*" + query + ".*";  // Prepare regex for case-insensitive search

	    // Define the match criteria for various fields
	    Criteria criteria = new Criteria().orOperator(
	        Criteria.where("payments.paymentId").regex(regexSearchTerm, "i"),
	        Criteria.where("userName").regex(regexSearchTerm, "i"),
	        Criteria.where("orderId").regex(regexSearchTerm, "i"),
	        Criteria.where("payments.contactEmail").regex(regexSearchTerm, "i")
	    );

	    Aggregation aggregation = Aggregation.newAggregation(
	        Aggregation.unwind("payments"), 
	        Aggregation.match(criteria),    
	        Aggregation.project("payments") 
	            .and("payments.paymentId").as("paymentId")
	            .and("payments.status").as("status")
	            .and("payments.type").as("type")
	            .and("payments.invoiceId").as("invoiceId")
	            .and("payments.amountRefunded").as("amountRefunded")
	            .and("payments.amountPaid").as("amountPaid")
	            .and("payments.refundStatus").as("refundStatus")
	            .and("payments.isCaptured").as("isCaptured")
	            .and("payments.contactEmail").as("contactEmail")
	            .and("payments.contactNo").as("contactNo")
	            .and("payments.fee").as("fee")
	            .and("payments.tax").as("tax")
	            .and("payments.bankTransactionId").as("bankTransactionId")
	            .and("payments.createdDate").as("createdDate")
	            .and("payments.notes").as("notes")
	            .and("payments.paymentSignature").as("paymentSignature")
	            .and("payments.bank").as("bank")
	            .and("payments.vpa").as("vpa")
	            .and("payments.cardId").as("cardId")
	            .and("payments.customerId").as("customerId")
	            .and("payments.tokenId").as("tokenId")
	            .and("payments.acquirerData").as("acquirerData")
	            .and("payments.upiVpa").as("upiVpa")
	            .and("payments.errorCode").as("errorCode")
	            .and("payments.errorDescription").as("errorDescription")
	            .and("payments.errorSource").as("errorSource")
	            .and("payments.errorStep").as("errorStep")
	            .and("payments.errorReason").as("errorReason")
	    );

	    AggregationResults<PaymentTransaction> results = mongoTemplate.aggregate(aggregation, "masterpayment", PaymentTransaction.class);

	    List<PaymentTransaction> paymentTransactions = results.getMappedResults();
	    logger.debug("Aggregation results: {}", paymentTransactions);
	    return paymentTransactions;
	}

	@Override
	public Map<String, Object> genericOrderSearch(String query) {
	    String regexSearchTerm = ".*" + query + ".*";  

	    
	    Criteria criteria = new Criteria().orOperator(
	        Criteria.where("payments.paymentId").regex(regexSearchTerm, "i"),
	        Criteria.where("userName").regex(regexSearchTerm, "i"),
	        Criteria.where("orderId").regex(regexSearchTerm, "i"),
	        Criteria.where("payments.contactEmail").regex(regexSearchTerm, "i")
	    );

	    Aggregation aggregation = Aggregation.newAggregation(
	        Aggregation.match(criteria),  
	        Aggregation.unwind("payments", "preserveNullAndEmptyArrays"),
	        Aggregation.group("_id") 
	            .first("orderId").as("orderId")
	            .first("formId").as("formId")
	            .first("userName").as("userName")
	            .first("amountCharged").as("amountCharged")
	            .first("amountDue").as("amountDue")
	            .first("amountPaid").as("amountPaid")
	            .first("paymentMethod").as("paymentMethod")
	            .first("currency").as("currency")
	            .first("orderAttempts").as("orderAttempts")
	            .first("createdAt").as("createdAt")
	            .first("status").as("status")
	            .first("formOrderNumber").as("formOrderNumber")
	            .first("totalAmountPaid").as("totalAmountPaid")
	            .first("tokenId").as("tokenId")
	            .push("payments").as("payments"),
	        Aggregation.project()
	            .and("orderId").as("orderId")
	            .and("formId").as("formId")
	            .and("userName").as("userName")
	            .and("amountCharged").as("amountCharged")
	            .and("amountDue").as("amountDue")
	            .and("amountPaid").as("amountPaid")
	            .and("paymentMethod").as("paymentMethod")
	            .and("currency").as("currency")
	            .and("orderAttempts").as("orderAttempts")
	            .and("createdAt").as("createdAt")
	            .and("status").as("status")
	            .and("formOrderNumber").as("formOrderNumber")
	            .and("totalAmountPaid").as("totalAmountPaid")
	            .and("tokenId").as("tokenId")
	            .and("payments").as("payments") 
	    );

	    AggregationResults<MasterPayment> results = mongoTemplate.aggregate(aggregation, "masterpayment", MasterPayment.class);

	    List<MasterPayment> masterPayments = results.getMappedResults();
	    logger.debug("Full Aggregation Results: {}", masterPayments);

	    if (masterPayments.isEmpty()) {
	        logger.debug("No results found for the query: {}", query);
	    }
	    
	    long orderCount = masterPayments.size();
	    logger.debug("Orders count Found for pagination: {}", orderCount);

	    // Prepare response
	    Map<String, Object> pagedOrdersWithCount = new HashMap<>();
	    pagedOrdersWithCount.put("OrdersCount", orderCount);
	    pagedOrdersWithCount.put("OrderData", masterPayments);

	    return pagedOrdersWithCount;
	}

	



	

//	@Override
//	 public List<MasterPayment> getPaymentByStatus(String orderId, String status) {
//        // Create the query
//        Query query = new Query();
//        query.addCriteria(Criteria.where("orderId").is(orderId)
//                .and("payments.status").is(status));
//        query.fields().include("payments"); // Project only the payments field
//
//        // Execute the query
//        return mongoTemplate.find(query, MasterPayment.class);
//    }
//	@Override
//	public List<PaymentTransaction> getCapturedPayments(String orderId, String orgRoleId) {
//        // Match stage to filter by orderId and organization roles
//        MatchOperation matchStage = Aggregation.match(Criteria.where("orderId").is(orderId)
//                .and("organization.roles.orgRoleId").is(orgRoleId));
//
//        // Projection stage to filter payments with status "captured"
//        ProjectionOperation projectStage = Aggregation.project()
//            .and(Aggregation.filter("payments")
//                .as("payment")
//                .by(Criteria.where("status").is("captured")))
//            .as("payments");
//
//        // Build the aggregation pipeline
//        Aggregation aggregation = Aggregation.newAggregation(matchStage, projectStage);
//
//        // Execute the aggregation
//        AggregationResults<PaymentTransaction> results = mongoTemplate.aggregate(aggregation, "masterpayment", PaymentTransaction.class);
//
//        // Return the mapped results
//        return results.getMappedResults();
//    }

	



    

}
