package com.example.bdkipoc.transaction.managers;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;

/**
 * MidtransCardPaymentManager - Enhanced Version with Backend Integration
 * 
 * Key Features:
 * - Uses static CVV "493" for all transactions (both EMV and regular cards)
 * - EMV-first approach with tokenization fallback
 * - Supports custom order_id from backend transaction_uuid
 * - Handles Midtrans API requirements where CVV is mandatory even for EMV
 * - Comprehensive error handling and retry logic
 * - Enhanced response processing with bank detection
 */
public class MidtransCardPaymentManager {
    private static final String TAG = "MidtransCardPayment";
    
    // Midtrans Configuration
    private static final String MIDTRANS_BASE_URL = "https://api.sandbox.midtrans.com";
    private static final String MIDTRANS_TOKEN_URL = MIDTRANS_BASE_URL + "/v2/token";
    private static final String MIDTRANS_CHARGE_URL = MIDTRANS_BASE_URL + "/v2/charge";
    private static final String MIDTRANS_CLIENT_KEY = "SB-Mid-client-zPs7DafB_fag5kOP";
    private static final String MIDTRANS_SERVER_AUTH = "Basic U0ItTWlkLXNlcnZlci1PM2t1bXkwVDl4M1VvYnVvVTc3NW5QbXc6";
    
    // EMV-specific configuration
    private static final String STATIC_CVV = "493"; // Static CVV for all tokenization (as per curl example)
    
    private Context context;
    private MidtransCardPaymentCallback callback;
    private int retryCount = 0;
    private static final int MAX_RETRY = 2;
    
    // ✅ ENHANCED: Store last response and custom order ID
    private JSONObject lastResponse;
    private String lastErrorMessage;
    private String customOrderId; // ✅ NEW: For backend transaction_uuid
    
    public interface MidtransCardPaymentCallback {
        void onTokenizeSuccess(String cardToken);
        void onTokenizeError(String errorMessage);
        void onChargeSuccess(JSONObject chargeResponse);
        void onChargeError(String errorMessage);
        void onPaymentProgress(String message);
    }
    
    public MidtransCardPaymentManager(Context context, MidtransCardPaymentCallback callback) {
        this.context = context;
        this.callback = callback;
    }
    
    // ✅ ENHANCED: Getter for last response
    public JSONObject getLastResponse() {
        return lastResponse;
    }
    
    // ✅ ENHANCED: Getter for last error message
    public String getLastErrorMessage() {
        return lastErrorMessage;
    }
    
    /**
     * ✅ NEW: Process EMV card payment with custom order ID (transaction_uuid from backend)
     */
    public void processEMVCardPaymentWithOrderId(CardData cardData, long amount, String orderId, String emvData) {
        this.customOrderId = orderId; // Store custom order ID
        
        Log.d(TAG, "=== PROCESSING EMV PAYMENT WITH CUSTOM ORDER ID ===");
        Log.d(TAG, "Custom Order ID (Transaction UUID): " + orderId);
        Log.d(TAG, "Amount: " + amount);
        Log.d(TAG, "Card PAN: " + maskCardNumber(cardData.getPan()));
        Log.d(TAG, "Payment Mode: EMV with Backend Integration");
        Log.d(TAG, "================================================");
        
        // Reset retry counter
        retryCount = 0;
        
        if (callback != null) {
            callback.onPaymentProgress("Processing EMV payment with backend UUID...");
        }
        
        // Use direct charge with custom order ID
        new EMVDirectChargeWithOrderIdTask(cardData, amount, orderId, emvData).execute();
    }
    
    /**
     * Process EMV card payment - handles EMV-specific requirements
     */
    public void processEMVCardPayment(CardData cardData, long amount, String referenceId, String emvData) {
        if (cardData == null || !cardData.isValid()) {
            if (callback != null) {
                callback.onChargeError("Invalid card data");
            }
            return;
        }
        
        // Reset retry counter and custom order ID
        retryCount = 0;
        customOrderId = null;
        
        Log.d(TAG, "=== STARTING EMV MIDTRANS PAYMENT ===");
        Log.d(TAG, "Reference ID: " + referenceId);
        Log.d(TAG, "Amount: " + amount);
        Log.d(TAG, "Card PAN: " + maskCardNumber(cardData.getPan()));
        Log.d(TAG, "Payment Mode: EMV with static CVV (" + STATIC_CVV + ")");
        Log.d(TAG, "==========================================");
        
        if (callback != null) {
            callback.onPaymentProgress("Processing EMV payment...");
        }
        
        // For EMV cards, try direct charge without tokenization first
        new EMVDirectChargeTask(cardData, amount, referenceId, emvData).execute();
    }
    
    /**
     * Process regular card payment with tokenization
     */
    public void processCardPayment(CardData cardData, long amount, String referenceId) {
        if (cardData == null || !cardData.isValid()) {
            if (callback != null) {
                callback.onChargeError("Invalid card data");
            }
            return;
        }
        
        retryCount = 0;
        customOrderId = null;
        
        Log.d(TAG, "=== STARTING REGULAR CARD PAYMENT ===");
        Log.d(TAG, "Using tokenization flow");
        
        if (callback != null) {
            callback.onPaymentProgress("Tokenizing card...");
        }
        
        // Use tokenization flow for regular cards
        new TokenizeCardTask(cardData, amount, referenceId).execute();
    }
    
    /**
     * ✅ NEW: EMV Direct Charge with Custom Order ID - uses backend transaction_uuid
     */
    private class EMVDirectChargeWithOrderIdTask extends AsyncTask<Void, Void, Boolean> {
        private CardData cardData;
        private long amount;
        private String orderId;
        private String emvData;
        private String errorMessage;
        private JSONObject chargeResponse;
        
        public EMVDirectChargeWithOrderIdTask(CardData cardData, long amount, String orderId, String emvData) {
            this.cardData = cardData;
            this.amount = amount;
            this.orderId = orderId;
            this.emvData = emvData;
        }
        
        @Override
        protected Boolean doInBackground(Void... voids) {
            try {
                // Build EMV-specific charge payload with custom order ID
                JSONObject payload = new JSONObject();
                payload.put("payment_type", "credit_card");
                
                // Transaction details with custom order ID
                JSONObject transactionDetails = new JSONObject();
                transactionDetails.put("order_id", orderId); // ✅ Use backend transaction_uuid
                transactionDetails.put("gross_amount", amount);
                payload.put("transaction_details", transactionDetails);
                
                // EMV Credit card data (no tokenization)
                JSONObject creditCard = new JSONObject();
                creditCard.put("card_number", cardData.getPan());
                creditCard.put("card_exp_month", cardData.getExpiryMonth());
                creditCard.put("card_exp_year", cardData.getExpiryYear());
                
                // Include static CVV even for EMV (Midtrans may require it)
                creditCard.put("card_cvv", STATIC_CVV);
                Log.d(TAG, "EMV Transaction: Including static CVV (" + STATIC_CVV + ") for Midtrans compatibility");
                
                // Add EMV data if available
                if (emvData != null && !emvData.isEmpty()) {
                    creditCard.put("emv_data", emvData);
                    creditCard.put("authentication_mode", "chip");
                }
                
                payload.put("credit_card", creditCard);
                
                // Item details
                JSONArray itemDetails = new JSONArray();
                JSONObject item = new JSONObject();
                item.put("id", "emv_backend1");
                item.put("price", amount);
                item.put("quantity", 1);
                item.put("name", "EMV Backend Transaction");
                item.put("brand", "EMV Backend Payment");
                item.put("category", "Backend Transaction");
                item.put("merchant_name", "EDC-Store-Backend");
                itemDetails.put(item);
                payload.put("item_details", itemDetails);
                
                // Customer details (same as curl example)
                addCustomerDetails(payload);
                
                Log.d(TAG, "=== EMV BACKEND DIRECT CHARGE ===");
                Log.d(TAG, "Order ID (Backend UUID): " + orderId);
                Log.d(TAG, "Amount: " + amount);
                Log.d(TAG, "Card: " + maskCardNumber(cardData.getPan()));
                Log.d(TAG, "Mode: EMV Backend Direct (No Token)");
                Log.d(TAG, "=================================");
                
                // Make charge request
                return makeChargeRequest(payload);
                
            } catch (Exception e) {
                Log.e(TAG, "EMV Backend Direct Charge exception: " + e.getMessage(), e);
                errorMessage = "EMV backend payment error: " + e.getMessage();
                return false;
            }
        }
        
        @Override
        protected void onPostExecute(Boolean success) {
            if (success && chargeResponse != null && callback != null) {
                Log.d(TAG, "✅ EMV Backend charge successful!");
                callback.onChargeSuccess(chargeResponse);
            } else if (callback != null) {
                // Fallback to tokenization if direct charge fails
                Log.w(TAG, "EMV backend direct charge failed, trying tokenization fallback...");
                callback.onPaymentProgress("Retrying with tokenization...");
                new TokenizeCardWithOrderIdTask(cardData, amount, orderId).execute();
            }
        }
        
        private Boolean makeChargeRequest(JSONObject payload) {
            try {
                URL url = new URI(MIDTRANS_CHARGE_URL).toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Accept", "application/json");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("Authorization", MIDTRANS_SERVER_AUTH);
                conn.setDoOutput(true);
                conn.setConnectTimeout(30000);
                conn.setReadTimeout(30000);
                
                try (OutputStream os = conn.getOutputStream()) {
                    byte[] input = payload.toString().getBytes("utf-8");
                    os.write(input, 0, input.length);
                }
                
                int responseCode = conn.getResponseCode();
                Log.d(TAG, "EMV Backend Charge response code: " + responseCode);
                
                BufferedReader br;
                StringBuilder response = new StringBuilder();
                String responseLine;
                
                if (responseCode == 200 || responseCode == 201) {
                    br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
                } else {
                    br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
                }
                
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                
                String responseString = response.toString();
                Log.d(TAG, "EMV Backend Charge response: " + responseString);
                
                // Store response for debugging
                try {
                    chargeResponse = new JSONObject(responseString);
                    lastResponse = chargeResponse;
                    
                    // Enhanced: Add bank detection if missing
                    enhanceResponseWithBankInfo(chargeResponse);
                    
                } catch (JSONException e) {
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
                    chargeResponse = createFallbackResponse(responseString, responseCode);
                    lastResponse = chargeResponse;
                }
                
                return processChargeResponse(chargeResponse, responseCode);
                
            } catch (Exception e) {
                Log.e(TAG, "EMV Backend Charge request exception: " + e.getMessage(), e);
                errorMessage = "Network error: " + e.getMessage();
                lastErrorMessage = errorMessage;
                return false;
            }
        }
        
        private Boolean processChargeResponse(JSONObject response, int httpCode) {
            try {
                String statusCode = response.optString("status_code", "");
                String statusMessage = response.optString("status_message", "");
                String transactionStatus = response.optString("transaction_status", "");
                String fraudStatus = response.optString("fraud_status", "");
                
                Log.d(TAG, "=== BACKEND CHARGE RESPONSE ANALYSIS ===");
                Log.d(TAG, "HTTP Code: " + httpCode);
                Log.d(TAG, "Status Code: " + statusCode);
                Log.d(TAG, "Status Message: " + statusMessage);
                Log.d(TAG, "Transaction Status: " + transactionStatus);
                Log.d(TAG, "Fraud Status: " + fraudStatus);
                Log.d(TAG, "Order ID: " + response.optString("order_id", ""));
                Log.d(TAG, "========================================");
                
                // Handle specific error cases
                if ("411".equals(statusCode)) {
                    errorMessage = "Token expired: " + statusMessage;
                    return false;
                } else if ("400".equals(statusCode)) {
                    errorMessage = "Bad request: " + statusMessage;
                    return false;
                } else if ("202".equals(statusCode) && "deny".equals(transactionStatus)) {
                    errorMessage = "Transaction denied: " + statusMessage;
                    return false;
                } else if (httpCode != 200 && httpCode != 201) {
                    errorMessage = "HTTP error: " + httpCode + " - " + statusMessage;
                    return false;
                }
                
                // Success conditions
                if ("200".equals(statusCode) && 
                    ("capture".equals(transactionStatus) || 
                     "settlement".equals(transactionStatus) || 
                     "pending".equals(transactionStatus))) {
                    return true;
                } else if ("201".equals(statusCode)) {
                    return true;
                } else {
                    errorMessage = "Transaction failed: " + statusMessage;
                    return false;
                }
                
            } catch (Exception e) {
                Log.e(TAG, "Error processing backend charge response: " + e.getMessage(), e);
                errorMessage = "Response processing error: " + e.getMessage();
                return false;
            }
        }
    }
    
    /**
     * ✅ NEW: Tokenize Card with Custom Order ID - fallback method
     */
    private class TokenizeCardWithOrderIdTask extends AsyncTask<Void, Void, String> {
        private CardData cardData;
        private long amount;
        private String orderId;
        private String errorMessage;
        
        public TokenizeCardWithOrderIdTask(CardData cardData, long amount, String orderId) {
            this.cardData = cardData;
            this.amount = amount;
            this.orderId = orderId;
        }
        
        @Override
        protected String doInBackground(Void... voids) {
            try {
                StringBuilder urlBuilder = new StringBuilder(MIDTRANS_TOKEN_URL);
                urlBuilder.append("?card_number=").append(cardData.getPan());
                urlBuilder.append("&card_exp_month=").append(cardData.getExpiryMonth());
                urlBuilder.append("&card_exp_year=").append(cardData.getExpiryYear());
                
                // Always include CVV for tokenization
                String cvvToUse = determineCVV(cardData);
                urlBuilder.append("&card_cvv=").append(cvvToUse);
                Log.d(TAG, "Using CVV " + cvvToUse + " for backend tokenization");
                
                urlBuilder.append("&client_key=").append(MIDTRANS_CLIENT_KEY);
                
                Log.d(TAG, "Backend Tokenization URL: " + maskUrl(urlBuilder.toString()));
                
                URL url = new URI(urlBuilder.toString()).toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setRequestProperty("Accept", "application/json");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setConnectTimeout(30000);
                conn.setReadTimeout(30000);
                
                int responseCode = conn.getResponseCode();
                Log.d(TAG, "Backend Tokenization response code: " + responseCode);
                
                if (responseCode == 200) {
                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
                    StringBuilder response = new StringBuilder();
                    String responseLine;
                    while ((responseLine = br.readLine()) != null) {
                        response.append(responseLine.trim());
                    }
                    
                    Log.d(TAG, "Backend Tokenization success response: " + response.toString());
                    
                    JSONObject jsonResponse = new JSONObject(response.toString());
                    if (jsonResponse.has("token_id")) {
                        return jsonResponse.getString("token_id");
                    } else {
                        errorMessage = "Token ID not found in backend response";
                        return null;
                    }
                    
                } else {
                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
                    StringBuilder errorResponse = new StringBuilder();
                    String responseLine;
                    while ((responseLine = br.readLine()) != null) {
                        errorResponse.append(responseLine.trim());
                    }
                    
                    Log.e(TAG, "Backend Tokenization error: " + errorResponse.toString());
                    errorMessage = "Backend tokenization failed: " + errorResponse.toString();
                    return null;
                }
                
            } catch (Exception e) {
                Log.e(TAG, "Backend Tokenization exception: " + e.getMessage(), e);
                errorMessage = "Network error: " + e.getMessage();
                return null;
            }
        }
        
        @Override
        protected void onPostExecute(String cardToken) {
            if (cardToken != null && callback != null) {
                callback.onTokenizeSuccess(cardToken);
                
                if (callback != null) {
                    callback.onPaymentProgress("Processing backend payment...");
                }
                new ChargeCardWithOrderIdTask(cardToken, amount, orderId, cardData).execute();
                
            } else if (callback != null) {
                callback.onTokenizeError(errorMessage != null ? errorMessage : "Unknown backend tokenization error");
            }
        }
    }
    
    /**
     * ✅ NEW: Charge Card with Custom Order ID
     */
    private class ChargeCardWithOrderIdTask extends AsyncTask<Void, Void, Boolean> {
        private String cardToken;
        private long amount;
        private String orderId;
        private String errorMessage;
        private JSONObject chargeResponse;
        private CardData cardData;
        
        public ChargeCardWithOrderIdTask(String cardToken, long amount, String orderId, CardData cardData) {
            this.cardToken = cardToken;
            this.amount = amount;
            this.orderId = orderId;
            this.cardData = cardData;
        }
        
        @Override
        protected Boolean doInBackground(Void... voids) {
            try {
                JSONObject payload = new JSONObject();
                payload.put("payment_type", "credit_card");
                
                JSONObject transactionDetails = new JSONObject();
                transactionDetails.put("order_id", orderId); // ✅ Use backend transaction_uuid
                transactionDetails.put("gross_amount", amount);
                payload.put("transaction_details", transactionDetails);
                
                JSONObject creditCard = new JSONObject();
                creditCard.put("token_id", cardToken);
                payload.put("credit_card", creditCard);
                
                // Item details
                JSONArray itemDetails = new JSONArray();
                JSONObject item = new JSONObject();
                item.put("id", "tkn_backend1");
                item.put("price", amount);
                item.put("quantity", 1);
                item.put("name", "Token Backend Transaction");
                item.put("brand", "Token Backend Payment");
                item.put("category", "Backend Transaction");
                item.put("merchant_name", "EDC-Store-Backend");
                itemDetails.put(item);
                payload.put("item_details", itemDetails);
                
                addCustomerDetails(payload);
                
                Log.d(TAG, "=== TOKEN BACKEND CHARGE ===");
                Log.d(TAG, "Order ID (Backend UUID): " + orderId);
                Log.d(TAG, "Amount: " + amount);
                Log.d(TAG, "Token: " + maskToken(cardToken));
                Log.d(TAG, "============================");
                
                return makeChargeRequest(payload);
                
            } catch (Exception e) {
                Log.e(TAG, "Token backend charge exception: " + e.getMessage(), e);
                errorMessage = "Token backend charge error: " + e.getMessage();
                return false;
            }
        }
        
        @Override
        protected void onPostExecute(Boolean success) {
            if (success && chargeResponse != null && callback != null) {
                Log.d(TAG, "✅ Token backend charge successful!");
                callback.onChargeSuccess(chargeResponse);
            } else if (callback != null) {
                // Check for retry scenarios
                if (shouldRetry(errorMessage) && retryCount < MAX_RETRY) {
                    retryCount++;
                    Log.w(TAG, "Retrying backend charge... (attempt " + retryCount + "/" + MAX_RETRY + ")");
                    callback.onPaymentProgress("Retrying backend... (" + retryCount + "/" + MAX_RETRY + ")");
                    new TokenizeCardWithOrderIdTask(cardData, amount, orderId).execute();
                } else {
                    if (retryCount >= MAX_RETRY) {
                        errorMessage = "Max retry attempts reached. " + errorMessage;
                    }
                    callback.onChargeError(errorMessage != null ? errorMessage : "Unknown backend charge error");
                }
            }
        }
        
        private Boolean makeChargeRequest(JSONObject payload) {
            try {
                URL url = new URI(MIDTRANS_CHARGE_URL).toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Accept", "application/json");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("Authorization", MIDTRANS_SERVER_AUTH);
                conn.setDoOutput(true);
                conn.setConnectTimeout(30000);
                conn.setReadTimeout(30000);
                
                try (OutputStream os = conn.getOutputStream()) {
                    byte[] input = payload.toString().getBytes("utf-8");
                    os.write(input, 0, input.length);
                }
                
                int responseCode = conn.getResponseCode();
                Log.d(TAG, "Token Backend Charge response code: " + responseCode);
                
                BufferedReader br;
                StringBuilder response = new StringBuilder();
                String responseLine;
                
                if (responseCode == 200 || responseCode == 201) {
                    br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
                } else {
                    br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
                }
                
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                
                String responseString = response.toString();
                Log.d(TAG, "Token Backend Charge response: " + responseString);
                
                // Store response for debugging
                try {
                    chargeResponse = new JSONObject(responseString);
                    lastResponse = chargeResponse;
                    
                    // Enhanced: Add bank detection if missing
                    enhanceResponseWithBankInfo(chargeResponse);
                    
                } catch (JSONException e) {
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
                    chargeResponse = createFallbackResponse(responseString, responseCode);
                    lastResponse = chargeResponse;
                }
                
                return processChargeResponse(chargeResponse, responseCode);
                
            } catch (Exception e) {
                Log.e(TAG, "Token backend charge request exception: " + e.getMessage(), e);
                errorMessage = "Network error: " + e.getMessage();
                lastErrorMessage = errorMessage;
                return false;
            }
        }
        
        private Boolean processChargeResponse(JSONObject response, int httpCode) {
            try {
                String statusCode = response.optString("status_code", "");
                String statusMessage = response.optString("status_message", "");
                String transactionStatus = response.optString("transaction_status", "");
                
                Log.d(TAG, "Token Backend Charge Response - Status: " + statusCode + ", Message: " + statusMessage);
                
                if ("411".equals(statusCode)) {
                    errorMessage = "Token expired: " + statusMessage;
                    return false;
                } else if ("400".equals(statusCode)) {
                    errorMessage = "Bad request: " + statusMessage;
                    return false;
                } else if ("202".equals(statusCode) && "deny".equals(transactionStatus)) {
                    errorMessage = "Transaction denied: " + statusMessage;
                    return false;
                } else if (httpCode != 200 && httpCode != 201) {
                    errorMessage = "HTTP error: " + httpCode + " - " + statusMessage;
                    return false;
                }
                
                if ("200".equals(statusCode) && 
                    ("capture".equals(transactionStatus) || 
                     "settlement".equals(transactionStatus) || 
                     "pending".equals(transactionStatus))) {
                    return true;
                } else if ("201".equals(statusCode)) {
                    return true;
                } else {
                    errorMessage = "Transaction failed: " + statusMessage;
                    return false;
                }
                
            } catch (Exception e) {
                Log.e(TAG, "Error processing token backend charge response: " + e.getMessage(), e);
                errorMessage = "Response processing error: " + e.getMessage();
                return false;
            }
        }
    }
    
    /**
     * EMV Direct Charge - bypasses tokenization for EMV cards
     */
    private class EMVDirectChargeTask extends AsyncTask<Void, Void, Boolean> {
        private CardData cardData;
        private long amount;
        private String referenceId;
        private String emvData;
        private String errorMessage;
        private JSONObject chargeResponse;
        
        public EMVDirectChargeTask(CardData cardData, long amount, String referenceId, String emvData) {
            this.cardData = cardData;
            this.amount = amount;
            this.referenceId = referenceId;
            this.emvData = emvData;
        }
        
        @Override
        protected Boolean doInBackground(Void... voids) {
            try {
                String orderId = "EMV" + System.currentTimeMillis();
                
                // Build EMV-specific charge payload
                JSONObject payload = new JSONObject();
                payload.put("payment_type", "credit_card");
                
                // Transaction details
                JSONObject transactionDetails = new JSONObject();
                transactionDetails.put("order_id", orderId);
                transactionDetails.put("gross_amount", amount);
                payload.put("transaction_details", transactionDetails);
                
                // EMV Credit card data (no tokenization)
                JSONObject creditCard = new JSONObject();
                creditCard.put("card_number", cardData.getPan());
                creditCard.put("card_exp_month", cardData.getExpiryMonth());
                creditCard.put("card_exp_year", cardData.getExpiryYear());
                
                // Include static CVV even for EMV (Midtrans may require it)
                creditCard.put("card_cvv", STATIC_CVV);
                Log.d(TAG, "EMV Transaction: Including static CVV (" + STATIC_CVV + ") for Midtrans compatibility");
                
                // Add EMV data if available
                if (emvData != null && !emvData.isEmpty()) {
                    creditCard.put("emv_data", emvData);
                    creditCard.put("authentication_mode", "chip");
                }
                
                payload.put("credit_card", creditCard);
                
                // Item details
                JSONArray itemDetails = new JSONArray();
                JSONObject item = new JSONObject();
                item.put("id", "emv1");
                item.put("price", amount);
                item.put("quantity", 1);
                item.put("name", "EMV Transaction");
                item.put("brand", "EMV Payment");
                item.put("category", "Transaction");
                item.put("merchant_name", "EDC-Store");
                itemDetails.put(item);
                payload.put("item_details", itemDetails);
                
                // Customer details (same as curl example)
                addCustomerDetails(payload);
                
                Log.d(TAG, "=== EMV DIRECT CHARGE ===");
                Log.d(TAG, "Order ID: " + orderId);
                Log.d(TAG, "Amount: " + amount);
                Log.d(TAG, "Card: " + maskCardNumber(cardData.getPan()));
                Log.d(TAG, "Mode: EMV Direct (No Token)");
                Log.d(TAG, "========================");
                
                // Make charge request
                return makeChargeRequest(payload);
                
            } catch (Exception e) {
                Log.e(TAG, "EMV Direct Charge exception: " + e.getMessage(), e);
                errorMessage = "EMV payment error: " + e.getMessage();
                return false;
            }
        }
        
        @Override
        protected void onPostExecute(Boolean success) {
            if (success && chargeResponse != null && callback != null) {
                callback.onChargeSuccess(chargeResponse);
            } else if (callback != null) {
                // Fallback to tokenization if direct charge fails
                Log.w(TAG, "EMV direct charge failed, trying tokenization fallback...");
                callback.onPaymentProgress("Retrying with tokenization...");
                new TokenizeCardTask(cardData, amount, referenceId).execute();
            }
        }
        
        private Boolean makeChargeRequest(JSONObject payload) {
            try {
                URL url = new URI(MIDTRANS_CHARGE_URL).toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Accept", "application/json");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("Authorization", MIDTRANS_SERVER_AUTH);
                conn.setDoOutput(true);
                conn.setConnectTimeout(30000);
                conn.setReadTimeout(30000);
                
                try (OutputStream os = conn.getOutputStream()) {
                    byte[] input = payload.toString().getBytes("utf-8");
                    os.write(input, 0, input.length);
                }
                
                int responseCode = conn.getResponseCode();
                Log.d(TAG, "EMV Charge response code: " + responseCode);
                
                BufferedReader br;
                StringBuilder response = new StringBuilder();
                String responseLine;
                
                if (responseCode == 200 || responseCode == 201) {
                    br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
                } else {
                    br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
                }
                
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                
                String responseString = response.toString();
                Log.d(TAG, "EMV Charge response: " + responseString);
                
                // Store response for debugging
                try {
                    chargeResponse = new JSONObject(responseString);
                    lastResponse = chargeResponse;
                    
                    // Enhanced: Add bank detection if missing
                    enhanceResponseWithBankInfo(chargeResponse);
                    
                } catch (JSONException e) {
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
                    chargeResponse = createFallbackResponse(responseString, responseCode);
                    lastResponse = chargeResponse;
                }
                
                return processChargeResponse(chargeResponse, responseCode);
                
            } catch (Exception e) {
                Log.e(TAG, "EMV Charge request exception: " + e.getMessage(), e);
                errorMessage = "Network error: " + e.getMessage();
                lastErrorMessage = errorMessage;
                return false;
            }
        }
        
        private Boolean processChargeResponse(JSONObject response, int httpCode) {
            try {
                String statusCode = response.optString("status_code", "");
                String statusMessage = response.optString("status_message", "");
                String transactionStatus = response.optString("transaction_status", "");
                String fraudStatus = response.optString("fraud_status", "");
                
                Log.d(TAG, "=== CHARGE RESPONSE ANALYSIS ===");
                Log.d(TAG, "HTTP Code: " + httpCode);
                Log.d(TAG, "Status Code: " + statusCode);
                Log.d(TAG, "Status Message: " + statusMessage);
                Log.d(TAG, "Transaction Status: " + transactionStatus);
                Log.d(TAG, "Fraud Status: " + fraudStatus);
                Log.d(TAG, "===============================");
                
                // Handle specific error cases
                if ("411".equals(statusCode)) {
                    errorMessage = "Token expired: " + statusMessage;
                    return false;
                } else if ("400".equals(statusCode)) {
                    errorMessage = "Bad request: " + statusMessage;
                    return false;
                } else if ("202".equals(statusCode) && "deny".equals(transactionStatus)) {
                    errorMessage = "Transaction denied: " + statusMessage;
                    return false;
                } else if (httpCode != 200 && httpCode != 201) {
                    errorMessage = "HTTP error: " + httpCode + " - " + statusMessage;
                    return false;
                }
                
                // Success conditions
                if ("200".equals(statusCode) && 
                    ("capture".equals(transactionStatus) || 
                     "settlement".equals(transactionStatus) || 
                     "pending".equals(transactionStatus))) {
                    return true;
                } else if ("201".equals(statusCode)) {
                    return true;
                } else {
                    errorMessage = "Transaction failed: " + statusMessage;
                    return false;
                }
                
            } catch (Exception e) {
                Log.e(TAG, "Error processing charge response: " + e.getMessage(), e);
                errorMessage = "Response processing error: " + e.getMessage();
                return false;
            }
        }
    }
    
    /**
     * Enhanced Tokenize Card Task with better CVV handling
     */
    private class TokenizeCardTask extends AsyncTask<Void, Void, String> {
        private CardData cardData;
        private long amount;
        private String referenceId;
        private String errorMessage;
        
        public TokenizeCardTask(CardData cardData, long amount, String referenceId) {
            this.cardData = cardData;
            this.amount = amount;
            this.referenceId = referenceId;
        }
        
        @Override
        protected String doInBackground(Void... voids) {
            try {
                StringBuilder urlBuilder = new StringBuilder(MIDTRANS_TOKEN_URL);
                urlBuilder.append("?card_number=").append(cardData.getPan());
                urlBuilder.append("&card_exp_month=").append(cardData.getExpiryMonth());
                urlBuilder.append("&card_exp_year=").append(cardData.getExpiryYear());
                
                // Always include CVV for tokenization (Midtrans requires it)
                String cvvToUse = determineCVV(cardData);
                urlBuilder.append("&card_cvv=").append(cvvToUse);
                Log.d(TAG, "Using CVV " + cvvToUse + " for tokenization (required by Midtrans)");
                
                urlBuilder.append("&client_key=").append(MIDTRANS_CLIENT_KEY);
                
                Log.d(TAG, "Tokenization URL: " + maskUrl(urlBuilder.toString()));
                
                URL url = new URI(urlBuilder.toString()).toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setRequestProperty("Accept", "application/json");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setConnectTimeout(30000);
                conn.setReadTimeout(30000);
                
                int responseCode = conn.getResponseCode();
                Log.d(TAG, "Tokenization response code: " + responseCode);
                
                if (responseCode == 200) {
                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
                    StringBuilder response = new StringBuilder();
                    String responseLine;
                    while ((responseLine = br.readLine()) != null) {
                        response.append(responseLine.trim());
                    }
                    
                    Log.d(TAG, "Tokenization success response: " + response.toString());
                    
                    JSONObject jsonResponse = new JSONObject(response.toString());
                    if (jsonResponse.has("token_id")) {
                        return jsonResponse.getString("token_id");
                    } else {
                        errorMessage = "Token ID not found in response";
                        return null;
                    }
                    
                } else {
                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
                    StringBuilder errorResponse = new StringBuilder();
                    String responseLine;
                    while ((responseLine = br.readLine()) != null) {
                        errorResponse.append(responseLine.trim());
                    }
                    
                    Log.e(TAG, "Tokenization error: " + errorResponse.toString());
                    errorMessage = "Tokenization failed: " + errorResponse.toString();
                    return null;
                }
                
            } catch (Exception e) {
                Log.e(TAG, "Tokenization exception: " + e.getMessage(), e);
                errorMessage = "Network error: " + e.getMessage();
                return null;
            }
        }
        
        @Override
        protected void onPostExecute(String cardToken) {
            if (cardToken != null && callback != null) {
                callback.onTokenizeSuccess(cardToken);
                
                if (callback != null) {
                    callback.onPaymentProgress("Processing payment...");
                }
                new ChargeCardTask(cardToken, amount, referenceId, cardData).execute();
                
            } else if (callback != null) {
                callback.onTokenizeError(errorMessage != null ? errorMessage : "Unknown tokenization error");
            }
        }
    }
    
    /**
     * Enhanced Charge Card Task
     */
    private class ChargeCardTask extends AsyncTask<Void, Void, Boolean> {
        private String cardToken;
        private long amount;
        private String referenceId;
        private String errorMessage;
        private JSONObject chargeResponse;
        private CardData cardData;
        
        public ChargeCardTask(String cardToken, long amount, String referenceId, CardData cardData) {
            this.cardToken = cardToken;
            this.amount = amount;
            this.referenceId = referenceId;
            this.cardData = cardData;
        }
        
        @Override
        protected Boolean doInBackground(Void... voids) {
            try {
                String orderId = "TKN" + System.currentTimeMillis();
                
                JSONObject payload = new JSONObject();
                payload.put("payment_type", "credit_card");
                
                JSONObject transactionDetails = new JSONObject();
                transactionDetails.put("order_id", orderId);
                transactionDetails.put("gross_amount", amount);
                payload.put("transaction_details", transactionDetails);
                
                JSONObject creditCard = new JSONObject();
                creditCard.put("token_id", cardToken);
                payload.put("credit_card", creditCard);
                
                // Item details
                JSONArray itemDetails = new JSONArray();
                JSONObject item = new JSONObject();
                item.put("id", "tkn1");
                item.put("price", amount);
                item.put("quantity", 1);
                item.put("name", "Token Transaction");
                item.put("brand", "Token Payment");
                item.put("category", "Transaction");
                item.put("merchant_name", "EDC-Store");
                itemDetails.put(item);
                payload.put("item_details", itemDetails);
                
                addCustomerDetails(payload);
                
                Log.d(TAG, "=== TOKEN CHARGE ===");
                Log.d(TAG, "Order ID: " + orderId);
                Log.d(TAG, "Amount: " + amount);
                Log.d(TAG, "Token: " + maskToken(cardToken));
                Log.d(TAG, "===================");
                
                return makeChargeRequest(payload);
                
            } catch (Exception e) {
                Log.e(TAG, "Token charge exception: " + e.getMessage(), e);
                errorMessage = "Token charge error: " + e.getMessage();
                return false;
            }
        }
        
        @Override
        protected void onPostExecute(Boolean success) {
            if (success && chargeResponse != null && callback != null) {
                callback.onChargeSuccess(chargeResponse);
            } else if (callback != null) {
                // Check for retry scenarios
                if (shouldRetry(errorMessage) && retryCount < MAX_RETRY) {
                    retryCount++;
                    Log.w(TAG, "Retrying charge... (attempt " + retryCount + "/" + MAX_RETRY + ")");
                    callback.onPaymentProgress("Retrying... (" + retryCount + "/" + MAX_RETRY + ")");
                    new TokenizeCardTask(cardData, amount, referenceId).execute();
                } else {
                    if (retryCount >= MAX_RETRY) {
                        errorMessage = "Max retry attempts reached. " + errorMessage;
                    }
                    callback.onChargeError(errorMessage != null ? errorMessage : "Unknown charge error");
                }
            }
        }
        
        private Boolean makeChargeRequest(JSONObject payload) {
            try {
                URL url = new URI(MIDTRANS_CHARGE_URL).toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Accept", "application/json");
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("Authorization", MIDTRANS_SERVER_AUTH);
                conn.setDoOutput(true);
                conn.setConnectTimeout(30000);
                conn.setReadTimeout(30000);
                
                try (OutputStream os = conn.getOutputStream()) {
                    byte[] input = payload.toString().getBytes("utf-8");
                    os.write(input, 0, input.length);
                }
                
                int responseCode = conn.getResponseCode();
                Log.d(TAG, "Token Charge response code: " + responseCode);
                
                BufferedReader br;
                StringBuilder response = new StringBuilder();
                String responseLine;
                
                if (responseCode == 200 || responseCode == 201) {
                    br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
                } else {
                    br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
                }
                
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                
                String responseString = response.toString();
                Log.d(TAG, "Token Charge response: " + responseString);
                
                // Store response for debugging
                try {
                    chargeResponse = new JSONObject(responseString);
                    lastResponse = chargeResponse;
                    
                    // Enhanced: Add bank detection if missing
                    enhanceResponseWithBankInfo(chargeResponse);
                    
                } catch (JSONException e) {
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
                    chargeResponse = createFallbackResponse(responseString, responseCode);
                    lastResponse = chargeResponse;
                }
                
                return processChargeResponse(chargeResponse, responseCode);
                
            } catch (Exception e) {
                Log.e(TAG, "Token charge request exception: " + e.getMessage(), e);
                errorMessage = "Network error: " + e.getMessage();
                lastErrorMessage = errorMessage;
                return false;
            }
        }
        
        private Boolean processChargeResponse(JSONObject response, int httpCode) {
            try {
                String statusCode = response.optString("status_code", "");
                String statusMessage = response.optString("status_message", "");
                String transactionStatus = response.optString("transaction_status", "");
                
                Log.d(TAG, "Token Charge Response - Status: " + statusCode + ", Message: " + statusMessage);
                
                if ("411".equals(statusCode)) {
                    errorMessage = "Token expired: " + statusMessage;
                    return false;
                } else if ("400".equals(statusCode)) {
                    errorMessage = "Bad request: " + statusMessage;
                    return false;
                } else if ("202".equals(statusCode) && "deny".equals(transactionStatus)) {
                    errorMessage = "Transaction denied: " + statusMessage;
                    return false;
                } else if (httpCode != 200 && httpCode != 201) {
                    errorMessage = "HTTP error: " + httpCode + " - " + statusMessage;
                    return false;
                }
                
                if ("200".equals(statusCode) && 
                    ("capture".equals(transactionStatus) || 
                     "settlement".equals(transactionStatus) || 
                     "pending".equals(transactionStatus))) {
                    return true;
                } else if ("201".equals(statusCode)) {
                    return true;
                } else {
                    errorMessage = "Transaction failed: " + statusMessage;
                    return false;
                }
                
            } catch (Exception e) {
                Log.e(TAG, "Error processing token charge response: " + e.getMessage(), e);
                errorMessage = "Response processing error: " + e.getMessage();
                return false;
            }
        }
    }
    
    // Method to enhance response with bank information
    private void enhanceResponseWithBankInfo(JSONObject response) {
        if (response == null) return;
        
        try {
            // If bank field is missing or empty, try to determine it
            if (!response.has("bank") || response.getString("bank").trim().isEmpty()) {
                String detectedBank = detectBankFromResponse(response);
                if (detectedBank != null) {
                    response.put("bank", detectedBank);
                    Log.d(TAG, "✅ Enhanced response with detected bank: " + detectedBank);
                }
            } else {
                String existingBank = response.getString("bank");
                Log.d(TAG, "✅ Response already has bank field: " + existingBank);
            }
            
            // Log final bank value
            if (response.has("bank")) {
                Log.d(TAG, "Final bank value in response: '" + response.getString("bank") + "'");
            }
            
        } catch (JSONException e) {
            Log.e(TAG, "Error enhancing response with bank info: " + e.getMessage());
        }
    }
    
    // Method to detect bank from various response fields
    private String detectBankFromResponse(JSONObject response) {
        try {
            // Try various fields that might contain bank information
            String[] possibleFields = {
                "issuer", "acquiring_bank", "card_type", "payment_method",
                "issuer_name", "bank_name", "acquirer"
            };
            
            for (String field : possibleFields) {
                if (response.has(field)) {
                    String value = response.getString(field);
                    if (value != null && !value.trim().isEmpty()) {
                        String mappedBank = mapToBankName(value);
                        if (mappedBank != null) {
                            Log.d(TAG, "Detected bank '" + mappedBank + "' from field '" + field + "': " + value);
                            return mappedBank;
                        }
                    }
                }
            }
            
            // If no bank detected from response fields, return default
            Log.d(TAG, "No bank detected from response fields, using default");
            return "BCA"; // Default
            
        } catch (JSONException e) {
            Log.e(TAG, "Error detecting bank from response: " + e.getMessage());
            return "BCA"; // Default
        }
    }
    
    // Map various bank identifiers to standard bank names
    private String mapToBankName(String identifier) {
        if (identifier == null || identifier.trim().isEmpty()) {
            return null;
        }
        
        String normalized = identifier.trim().toUpperCase();
        
        // Map common bank identifiers
        if (normalized.contains("BCA") || normalized.contains("CENTRAL ASIA")) {
            return "BCA";
        } else if (normalized.contains("MANDIRI")) {
            return "MANDIRI";
        } else if (normalized.contains("BNI") || normalized.contains("NEGARA INDONESIA")) {
            return "BNI";
        } else if (normalized.contains("BRI") || normalized.contains("RAKYAT INDONESIA")) {
            return "BRI";
        } else if (normalized.contains("CIMB") || normalized.contains("NIAGA")) {
            return "CIMB NIAGA";
        } else if (normalized.contains("DANAMON")) {
            return "DANAMON";
        } else if (normalized.contains("PERMATA")) {
            return "PERMATA";
        } else if (normalized.contains("MEGA")) {
            return "MEGA";
        }
        
        return null; // No mapping found
    }
    
    // Create fallback response when JSON parsing fails
    private JSONObject createFallbackResponse(String rawResponse, int httpCode) {
        try {
            JSONObject fallback = new JSONObject();
            fallback.put("status_code", String.valueOf(httpCode));
            fallback.put("raw_response", rawResponse);
            fallback.put("transaction_status", httpCode == 200 ? "capture" : "deny");
            fallback.put("bank", "BCA"); // Default bank
            fallback.put("is_fallback_response", true);
            
            Log.d(TAG, "Created fallback response for HTTP " + httpCode);
            return fallback;
            
        } catch (JSONException e) {
            Log.e(TAG, "Error creating fallback response: " + e.getMessage());
            return new JSONObject();
        }
    }
    
    // Helper Methods
    
    /**
     * Intelligent CVV determination - Always use static CVV for tokenization
     */
    private String determineCVV(CardData cardData) {
        // For tokenization, Midtrans always requires CVV even for EMV cards
        // Use static CVV as per curl example
        Log.d(TAG, "Using static CVV (493) for tokenization - required by Midtrans API");
        return STATIC_CVV;
    }
    
    /**
     * Add customer details to payload (extracted for reuse)
     */
    private void addCustomerDetails(JSONObject payload) throws JSONException {
        JSONObject customerDetails = new JSONObject();
        customerDetails.put("first_name", "BUDI");
        customerDetails.put("last_name", "UTOMO");
        customerDetails.put("email", "test@midtrans.com");
        customerDetails.put("phone", "+628123456");
        
        // Billing address
        JSONObject billingAddress = new JSONObject();
        billingAddress.put("first_name", "BUDI");
        billingAddress.put("last_name", "UTOMO");
        billingAddress.put("email", "test@midtrans.com");
        billingAddress.put("phone", "081 2233 44-55");
        billingAddress.put("address", "Sudirman");
        billingAddress.put("city", "Jakarta");
        billingAddress.put("postal_code", "12190");
        billingAddress.put("country_code", "IDN");
        customerDetails.put("billing_address", billingAddress);
        
        // Shipping address
        JSONObject shippingAddress = new JSONObject();
        shippingAddress.put("first_name", "BUDI");
        shippingAddress.put("last_name", "UTOMO");
        shippingAddress.put("email", "test@midtrans.com");
        shippingAddress.put("phone", "0 8128-75 7-9338");
        shippingAddress.put("address", "Sudirman");
        shippingAddress.put("city", "Jakarta");
        shippingAddress.put("postal_code", "12190");
        shippingAddress.put("country_code", "IDN");
        customerDetails.put("shipping_address", shippingAddress);
        
        payload.put("customer_details", customerDetails);
    }
    
    /**
     * Check if error should trigger a retry
     */
    private boolean shouldRetry(String error) {
        if (error == null) return false;
        
        return error.contains("Token expired") ||
               error.contains("Network error") ||
               error.contains("timeout");
    }
    
    // Debug method to inspect response
    public void debugResponse() {
        if (lastResponse != null) {
            Log.d(TAG, "=== DEBUGGING LAST RESPONSE ===");
            try {
                java.util.Iterator<String> keys = lastResponse.keys();
                while (keys.hasNext()) {
                    String key = keys.next();
                    Object value = lastResponse.get(key);
                    Log.d(TAG, key + ": " + value);
                }
            } catch (Exception e) {
                Log.e(TAG, "Error debugging response: " + e.getMessage());
            }
            Log.d(TAG, "===============================");
        } else {
            Log.d(TAG, "No last response available for debugging");
        }
    }
    
    /**
     * Enhanced Card data holder class with EMV detection
     */
    public static class CardData {
        private String pan;
        private String expiryMonth;
        private String expiryYear;
        private String cvv;
        private String cardholderName;
        private String aidIdentifier;
        private boolean isEMVCard = false;
        
        public CardData(String pan, String expiryMonth, String expiryYear, String cardholderName) {
            this.pan = pan;
            this.expiryMonth = expiryMonth;
            this.expiryYear = expiryYear;
            this.cardholderName = cardholderName;
        }
        
        public static CardData fromEMVData(String pan, String expiryDate, String cardholderName, String aid) {
            String expMonth = "";
            String expYear = "";
            
            if (expiryDate != null && expiryDate.length() == 6) {
                expYear = "20" + expiryDate.substring(0, 2);
                expMonth = expiryDate.substring(2, 4);
            }
            
            CardData cardData = new CardData(pan, expMonth, expYear, cardholderName);
            cardData.aidIdentifier = aid;
            cardData.isEMVCard = true; // Mark as EMV card
            return cardData;
        }
        
        public boolean isValid() {
            return pan != null && !pan.isEmpty() && 
                   expiryMonth != null && !expiryMonth.isEmpty() &&
                   expiryYear != null && !expiryYear.isEmpty();
        }
        
        // Getters
        public String getPan() { return pan; }
        public String getExpiryMonth() { return expiryMonth; }
        public String getExpiryYear() { return expiryYear; }
        public String getCvv() { return cvv; }
        public String getCardholderName() { return cardholderName; }
        public String getAidIdentifier() { return aidIdentifier; }
        public boolean isEMVCard() { return isEMVCard; }
        
        // Setters
        public void setCvv(String cvv) { this.cvv = cvv; }
        public void setEMVCard(boolean isEMVCard) { this.isEMVCard = isEMVCard; }
    }
    
    // Utility methods
    private String maskCardNumber(String cardNumber) {
        if (cardNumber == null || cardNumber.length() < 8) {
            return cardNumber;
        }
        String first4 = cardNumber.substring(0, 4);
        String last4 = cardNumber.substring(cardNumber.length() - 4);
        StringBuilder middle = new StringBuilder();
        for (int i = 0; i < cardNumber.length() - 8; i++) {
            middle.append("*");
        }
        return first4 + middle.toString() + last4;
    }
    
    private String maskToken(String token) {
        if (token == null || token.length() < 8) {
            return token;
        }
        return token.substring(0, 8) + "***";
    }
    
    private String maskUrl(String url) {
        if (url == null) return url;
        return url.replaceAll("card_number=[^&]*", "card_number=****")
                 .replaceAll("card_cvv=[^&]*", "card_cvv=***")
                 .replaceAll("client_key=[^&]*", "client_key=***");
    }
}