package com.morefun.tester;

import org.jpos.iso.ISOMsg;
import org.jpos.iso.channel.NACChannel;
import org.jpos.iso.packager.ISO87BPackager;
import org.jpos.util.LogEvent;
import org.jpos.util.Logger;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * jPOS Implementation Tester
 * Tests ISO8583 message creation and sending using jPOS library
 */
public class JPOSMessageTester {
    
    // Transaction data
    private static final String MERCHANT_ID = "900890089008900";
    private static final String TERMINAL_ID = "12345678";
    private static final String TEST_CARD = "4111111111111111";
    
    public void testSaleMessage() throws Exception {
        System.out.println("Creating jPOS Sale message...");
        
        ISOMsg saleMsg = new ISOMsg();
        saleMsg.setPackager(new ISO87BPackager());
        
        // Build Sale message
        buildSaleMessage(saleMsg);
        
        // Display message details
        displayMessage(saleMsg, "SALE");
        
        if (NetworkConfig.ENABLE_NETWORK) {
            sendMessage(saleMsg, "SALE");
        } else {
            System.out.println("[SUCCESS] Sale message created successfully (network disabled)");
        }
    }
    
    public void testReversalMessage() throws Exception {
        System.out.println("\nCreating jPOS Reversal message...");
        
        ISOMsg reversalMsg = new ISOMsg();
        reversalMsg.setPackager(new ISO87BPackager());
        
        // Build Reversal message
        buildReversalMessage(reversalMsg);
        
        // Display message details
        displayMessage(reversalMsg, "REVERSAL");
        
        if (NetworkConfig.ENABLE_NETWORK) {
            sendMessage(reversalMsg, "REVERSAL");
        } else {
            System.out.println("[SUCCESS] Reversal message created successfully (network disabled)");
        }
    }
    
    private void buildSaleMessage(ISOMsg msg) throws Exception {
        // Generate timestamps
        SimpleDateFormat dateFormat = new SimpleDateFormat("MMdd", Locale.US);
        SimpleDateFormat timeFormat = new SimpleDateFormat("HHmmss", Locale.US);
        Date now = new Date();
        
        // Generate trace number
        String traceNo = String.format("%06d", System.currentTimeMillis() % 1000000);
        
        // Build ISO8583 fields
        msg.setMTI("0200");                    // Financial transaction request
        msg.set(2, TEST_CARD);                 // PAN
        msg.set(3, "000000");                  // Processing code (Sale)
        msg.set(4, "000000001500");           // Amount ($15.00)
        msg.set(11, traceNo);                 // STAN
        msg.set(12, timeFormat.format(now));  // Transaction time
        msg.set(13, dateFormat.format(now));  // Transaction date
        msg.set(14, "2512");                  // Expiry date
        msg.set(22, "012");                   // POS entry mode
        msg.set(25, "00");                    // POS condition code
        msg.set(41, TERMINAL_ID);             // Terminal ID
        msg.set(42, MERCHANT_ID);             // Merchant ID
        msg.set(49, "840");                   // Currency (USD)
        msg.set(62, "000001");                // Batch number
    }
    
    private void buildReversalMessage(ISOMsg msg) throws Exception {
        // Generate timestamps
        SimpleDateFormat dateFormat = new SimpleDateFormat("MMdd", Locale.US);
        SimpleDateFormat timeFormat = new SimpleDateFormat("HHmmss", Locale.US);
        Date now = new Date();
        
        // Generate trace number (new for reversal)
        String traceNo = String.format("%06d", System.currentTimeMillis() % 1000000);
        
        // Build ISO8583 fields for reversal
        msg.setMTI("0400");                    // Reversal transaction request
        msg.set(2, TEST_CARD);                 // PAN (same as original)
        msg.set(3, "000000");                  // Processing code (same as original)
        msg.set(4, "000000001500");           // Amount (same as original)
        msg.set(11, traceNo);                 // STAN (new for reversal)
        msg.set(12, timeFormat.format(now));  // Transaction time
        msg.set(13, dateFormat.format(now));  // Transaction date
        msg.set(22, "012");                   // POS entry mode
        msg.set(25, "00");                    // POS condition code
        msg.set(41, TERMINAL_ID);             // Terminal ID
        msg.set(42, MERCHANT_ID);             // Merchant ID
        msg.set(49, "840");                   // Currency (USD)
        msg.set(62, "000001");                // Batch number
    }
    
    private void displayMessage(ISOMsg msg, String messageType) {
        System.out.println("\n--- " + messageType + " Message Details ---");
        
        try {
            System.out.println("MTI: " + msg.getMTI());
            System.out.println("PAN: " + maskPAN(msg.getString(2)));
            System.out.println("Amount: $" + formatAmount(msg.getString(4)));
            System.out.println("STAN: " + msg.getString(11));
            System.out.println("Time: " + msg.getString(12));
            System.out.println("Date: " + msg.getString(13));
            System.out.println("Terminal: " + msg.getString(41));
            System.out.println("Merchant: " + msg.getString(42));
            
            // Show all fields
            System.out.println("\nAll Fields:");
            for (int i = 2; i <= 128; i++) {
                if (msg.hasField(i)) {
                    String value = msg.getString(i);
                    if (i == 2) value = maskPAN(value); // Mask PAN
                    System.out.println(String.format("  Field %03d: %s", i, value));
                }
            }
        } catch (Exception e) {
            System.err.println("Error displaying message: " + e.getMessage());
        }
    }
    
    private void sendMessage(ISOMsg msg, String messageType) throws Exception {
        System.out.println("\n--- Sending " + messageType + " via jPOS ---");
        System.out.println("Target: " + NetworkConfig.SERVER_IP + ":" + NetworkConfig.SERVER_PORT);
        
        try {
            // Create NACChannel
            NACChannel channel = new NACChannel(new ISO87BPackager(), NetworkConfig.TPDU_HEADER.getBytes());
            channel.setHost(NetworkConfig.SERVER_IP);
            channel.setPort(NetworkConfig.SERVER_PORT);
            channel.setTimeout(NetworkConfig.CONNECTION_TIMEOUT);
            
            // Connect and send
            channel.connect();
            channel.send(msg);
            
            // Receive response
            ISOMsg response = channel.receive();
            
            // Process response
            processResponse(response, messageType);
            
        } catch (Exception e) {
            System.err.println("Network error: " + e.getMessage());
            throw e;
        }
    }
    
    private void processResponse(ISOMsg response, String messageType) {
        System.out.println("\n--- " + messageType + " Response ---");
        
        try {
            String responseCode = response.getString(39);
            String authCode = response.getString(38);
            
            System.out.println("Response Code: " + responseCode);
            System.out.println("Auth Code: " + authCode);
            
            if ("00".equals(responseCode)) {
                System.out.println("[SUCCESS] TRANSACTION APPROVED");
            } else {
                System.out.println("[DECLINED] TRANSACTION DECLINED");
            }
            
            // Log with jPOS Logger
            LogEvent logEvent = new LogEvent("response");
            logEvent.addMessage("Received " + messageType + " response: " + response);
            Logger.log(logEvent);
            
        } catch (Exception e) {
            System.err.println("Error processing response: " + e.getMessage());
        }
    }
    
    private String maskPAN(String pan) {
        if (pan == null || pan.length() < 8) return pan;
        return pan.substring(0, 4) + "****" + pan.substring(pan.length() - 4);
    }
    
    private String formatAmount(String amount) {
        if (amount == null) return "0.00";
        try {
            long cents = Long.parseLong(amount);
            return String.format("%.2f", cents / 100.0);
        } catch (Exception e) {
            return amount;
        }
    }
}