package com.morefun.tester;

import java.io.*;
import java.net.*;

/**
 * Mercury Exact Packet Builder - creates the exact packet format requested
 * Builds: 011460007820000400703C078000C08204164854980600736740000000000000000500000019085057091731050051000007820053313233343536375331323334353637383931323334353738340028950500800400009F1E0832333733303030319F100706011203A0A8020006303030303031
 */
public class MercuryExactPacket {
    
    public static void main(String[] args) {
        MercuryExactPacket builder = new MercuryExactPacket();
        
        try {
            System.out.println("=== Mercury Exact Packet Builder ===");
            System.out.println("Creating exact packet format as specified");
            System.out.println();
            
            // Create the exact packet
            byte[] packet = builder.createExactPacket();
            
            // Display and send
            builder.displayAndSend(packet);
            
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * Creates the exact Mercury packet as specified in the example
     */
    public byte[] createExactPacket() {
        System.out.println("Building exact Mercury ISO8583 packet...");
        
        // The exact hex string from your example
        String exactHexString = "011460007820000400703C078000C08204164854980600736740000000000000000500000019085057091731050051000007820053313233343536375331323334353637383931323334353738340028950500800400009F1E0832333733303030319F100706011203A0A8020006303030303031";
        
        // Convert to bytes
        byte[] packet = hexStringToByteArray(exactHexString);
        
        System.out.println("✓ Exact packet created: " + packet.length + " bytes");
        return packet;
    }
    
    /**
     * Alternative method: Build packet field by field matching the XML specification
     */
    public byte[] createFromFields() {
        System.out.println("Building Mercury packet from fields...");
        
        StringBuilder hex = new StringBuilder();
        
        // BCD Length: 0114 (276 bytes total length)
        hex.append("0114");
        
        // TPDU Header
        hex.append("6000782000");
        
        // MTI: 0400 (response message)
        hex.append("0400");
        
        // Primary Bitmap: 703C078000C08204
        hex.append("703C078000C08204");
        
        // Field 2: PAN (16 digits) with length prefix
        hex.append("16");  // Length
        hex.append("4854980600736740");
        
        // Field 3: Processing code (6 digits, padded)
        hex.append("000000");
        
        // Field 4: Transaction amount (12 digits)
        hex.append("000000006800");
        
        // Field 11: STAN (6 digits)
        hex.append("000075");
        
        // Field 12: Time (6 digits)
        hex.append("145824");
        
        // Field 13: Date (4 digits)  
        hex.append("0917");
        
        // Field 14: Expiration date (4 digits)
        hex.append("3105");
        
        // Field 22: POS entry mode (3 digits)
        hex.append("051");
        
        // Field 23: Card sequence number (3 digits)
        hex.append("000");
        
        // Field 24: Function code (3 digits)
        hex.append("782");
        
        // Field 25: POS condition code (2 digits)
        hex.append("00");
        
        // Field 35: Track 2 data with length prefix
        String track2 = "4854980600736740=31052060011790";
        hex.append(String.format("%02d", track2.length()));
        
        // Convert track2 to hex
        for (char c : track2.toCharArray()) {
            if (c == '=') {
                hex.append("D"); // = becomes D in track data
            } else {
                hex.append(c);
            }
        }
        
        // Field 41: Terminal ID (8 chars)
        hex.append("3132333435363731"); // "12345671" in hex
        
        // Field 42: Merchant ID (15 chars)  
        hex.append("313233343536373839313233343537383430"); // "123456789012345780" in hex
        
        // Field 49: Currency code (3 digits)
        hex.append("784");
        
        // Field 55: EMV data with length prefix
        String emvData = "950500800400009F1E0832333733303030319F100706011203A0A802";
        hex.append(String.format("%03d", emvData.length() / 2));
        hex.append(emvData);
        
        // Field 62: Batch number with length prefix
        hex.append("006"); // Length
        hex.append("303030303031"); // "000001" in hex
        
        byte[] packet = hexStringToByteArray(hex.toString());
        
        System.out.println("✓ Field-based packet created: " + packet.length + " bytes");
        return packet;
    }
    
    /**
     * Convert hex string to byte array
     */
    private byte[] hexStringToByteArray(String hex) {
        // Remove any spaces or non-hex characters
        hex = hex.replaceAll("[^0-9A-Fa-f]", "");
        
        int len = hex.length();
        if (len % 2 != 0) {
            hex = "0" + hex; // Pad with leading zero if odd length
            len = hex.length();
        }
        
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                                 + Character.digit(hex.charAt(i+1), 16));
        }
        return data;
    }
    
    /**
     * Convert byte array to hex string
     */
    private String byteArrayToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02X", b & 0xFF));
        }
        return result.toString();
    }
    
    /**
     * Display packet details and send if network enabled
     */
    private void displayAndSend(byte[] packet) {
        System.out.println("\n=== Packet Analysis ===");
        
        String hexString = byteArrayToHex(packet);
        System.out.println("Packet length: " + packet.length + " bytes");
        System.out.println("Hex representation:");
        
        // Format for display
        StringBuilder formatted = new StringBuilder();
        for (int i = 0; i < hexString.length(); i += 2) {
            if (i > 0 && i % 32 == 0) {
                formatted.append("\n");
            }
            formatted.append(hexString.substring(i, Math.min(i + 2, hexString.length()))).append(" ");
        }
        System.out.println(formatted.toString());
        
        // Analyze packet structure
        analyzePacketStructure(packet);
        
        // Send if network enabled
        if (NetworkConfig.ENABLE_NETWORK) {
            sendPacket(packet);
        } else {
            System.out.println("\n[INFO] Network disabled - set ENABLE_NETWORK = true to send");
        }
    }
    
    /**
     * Analyze the packet structure
     */
    private void analyzePacketStructure(byte[] packet) {
        System.out.println("\n=== Packet Structure Analysis ===");
        
        if (packet.length >= 2) {
            int length = ((packet[0] & 0xFF) << 8) | (packet[1] & 0xFF);
            System.out.printf("BCD Length: %02X %02X (%d decimal)%n", 
                            packet[0] & 0xFF, packet[1] & 0xFF, length);
        }
        
        if (packet.length >= 7) {
            System.out.print("TPDU Header: ");
            for (int i = 2; i < 7; i++) {
                System.out.printf("%02X ", packet[i] & 0xFF);
            }
            System.out.println();
        }
        
        if (packet.length >= 9) {
            System.out.printf("MTI: %02X%02X%n", packet[7] & 0xFF, packet[8] & 0xFF);
        }
        
        if (packet.length >= 17) {
            System.out.print("Primary Bitmap: ");
            for (int i = 9; i < 17; i++) {
                System.out.printf("%02X", packet[i] & 0xFF);
            }
            System.out.println();
        }
        
        // Compare with expected
        String expected = "011460007820000400703C078000C08204164854980600736740000000000000000500000019085057091731050051000007820053313233343536375331323334353637383931323334353738340028950500800400009F1E0832333733303030319F100706011203A0A8020006303030303031";
        String actual = byteArrayToHex(packet);
        
        System.out.println("\n=== Verification ===");
        System.out.println("Expected: " + expected);
        System.out.println("Actual:   " + actual);
        System.out.println("Match: " + (actual.equalsIgnoreCase(expected) ? "✓ EXACT MATCH" : "✗ DIFFERENT"));
        
        if (!actual.equalsIgnoreCase(expected)) {
            // Find first difference
            int minLen = Math.min(expected.length(), actual.length());
            for (int i = 0; i < minLen; i += 2) {
                String expByte = expected.substring(i, Math.min(i + 2, expected.length()));
                String actByte = actual.substring(i, Math.min(i + 2, actual.length()));
                if (!expByte.equalsIgnoreCase(actByte)) {
                    System.out.printf("First difference at position %d: expected %s, got %s%n", 
                                    i/2, expByte, actByte);
                    break;
                }
            }
        }
    }
    
    /**
     * Send packet via TCP
     */
    private void sendPacket(byte[] packet) {
        System.out.println("\n=== Sending Packet ===");
        System.out.printf("Target: %s:%d%n", NetworkConfig.SERVER_IP, NetworkConfig.SERVER_PORT);
        
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(NetworkConfig.SERVER_IP, NetworkConfig.SERVER_PORT), 
                          NetworkConfig.CONNECTION_TIMEOUT);
            socket.setSoTimeout(NetworkConfig.READ_TIMEOUT);
            
            System.out.println("✓ Connected to server");
            
            // Send packet
            OutputStream out = socket.getOutputStream();
            out.write(packet);
            out.flush();
            
            System.out.println("✓ Packet sent (" + packet.length + " bytes)");
            
            // Read response
            InputStream in = socket.getInputStream();
            byte[] buffer = new byte[2048];
            int bytesRead = in.read(buffer);
            
            if (bytesRead > 0) {
                byte[] response = new byte[bytesRead];
                System.arraycopy(buffer, 0, response, 0, bytesRead);
                
                System.out.println("✓ Response received (" + bytesRead + " bytes)");
                System.out.println("Response hex: " + byteArrayToHex(response));
                
                // Parse response if it looks like ISO8583
                if (bytesRead >= 9) {
                    System.out.printf("Response MTI: %02X%02X%n", 
                                    response[7] & 0xFF, response[8] & 0xFF);
                }
            } else {
                System.out.println("No response received");
            }
            
        } catch (Exception e) {
            System.err.println("Network error: " + e.getMessage());
            
            // Show details for common issues
            if (e instanceof ConnectException) {
                System.err.println("Connection refused - check if server is running");
            } else if (e instanceof SocketTimeoutException) {
                System.err.println("Timeout - server may be slow to respond");
            }
        }
    }
}