import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.WebSocket;
import java.time.LocalDate;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;

/**
 * Simplified Picondo API Client Example (No External Dependencies)
 * 
 * This version uses only Java's built-in libraries.
 * For production use, consider using a JSON library like Gson for proper parsing.
 * 
 * Usage:
 *   javac SimplePicondoClient.java
 *   java SimplePicondoClient
 */
public class SimplePicondoClient {

    private static final String API_BASE_URL = "http://127.0.0.1:7373";
    private final String API_KEY;
    
    private final HttpClient httpClient;
    
    public SimplePicondoClient(String apiKey) {
        API_KEY = apiKey;
        this.httpClient = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_1_1)
                .build();
    }

    // ========== REST API Example ==========
    
    /**
     * Example REST API call: Query QSOs by callsign
     */
    public String queryByCallsign(String callsign) throws Exception {
        String url = API_BASE_URL + "/api/qsos/callsign/" + callsign;
        
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("X-API-Key", API_KEY)
                .header("Accept", "application/json")
                .GET()
                .build();
        
        HttpResponse<String> response = httpClient.send(request, 
                HttpResponse.BodyHandlers.ofString());
        
        if (response.statusCode() != 200) {
            throw new RuntimeException("API Error: " + response.statusCode() 
                    + " - " + response.body());
        }
        
        return response.body();
    }
    
    /**
     * Example REST API call with date range
     */
    public String queryByDate(LocalDate startDate, LocalDate endDate) throws Exception {
        StringBuilder url = new StringBuilder(API_BASE_URL)
                .append("/api/qsos/date/")
                .append(startDate);
        
        if (endDate != null) {
            url.append("?endDate=").append(endDate);
        }
        
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url.toString()))
                .header("X-API-Key", API_KEY)
                .header("Accept", "application/json")
                .GET()
                .build();
        
        HttpResponse<String> response = httpClient.send(request, 
                HttpResponse.BodyHandlers.ofString());
        
        return response.body();
    }
    
    /**
     * Health check (no authentication required)
     */
    public String checkHealth() throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(API_BASE_URL + "/api/health"))
                .GET()
                .build();
        
        HttpResponse<String> response = httpClient.send(request, 
                HttpResponse.BodyHandlers.ofString());
        
        return response.body();
    }
    
    // ========== WebSocket Example ==========
    
    /**
     * Connect to WebSocket and listen for QSO events
     */
    public void connectWebSocket() throws Exception {
        String wsUrl = "ws://127.0.0.1:7373/api/events?apiKey=" + API_KEY;
        
        CountDownLatch latch = new CountDownLatch(1);
        final WebSocket[] webSocketRef = new WebSocket[1];
        
        // Add shutdown hook for graceful cleanup
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("\n\n✓ Shutting down gracefully...");
            if (webSocketRef[0] != null) {
                try {
                    // Send close frame before exiting
                    webSocketRef[0].sendClose(WebSocket.NORMAL_CLOSURE, "Client shutdown")
                            .get(5, java.util.concurrent.TimeUnit.SECONDS);
                } catch (Exception e) {
                    // Ignore errors during shutdown
                }
            }
            latch.countDown();
        }));
        
        webSocketRef[0] = httpClient.newWebSocketBuilder()
                .buildAsync(URI.create(wsUrl), new WebSocket.Listener() {
                    
                    private final StringBuilder messageBuffer = new StringBuilder();
                    
                    @Override
                    public void onOpen(WebSocket webSocket) {
                        System.out.println("\n✓ WebSocket Connected!");
                        System.out.println("Listening for QSO events... (Press Ctrl+C to exit)\n");
                        webSocket.request(1);
                    }
                    
                    @Override
                    public CompletableFuture<?> onText(WebSocket webSocket, 
                                                        CharSequence data, 
                                                        boolean last) {
                        messageBuffer.append(data);
                        
                        if (last) {
                            String message = messageBuffer.toString();
                            messageBuffer.setLength(0);
                            
                            // Simple parsing - in production use proper JSON library
                            System.out.println("┌─ NEW QSO EVENT ──────────────────────");
                            System.out.println("│ " + extractField(message, "callsign"));
                            System.out.println("│ " + extractField(message, "frequency"));
                            System.out.println("│ " + extractField(message, "mode"));
                            System.out.println("│ Timestamp: " + extractField(message, "timestamp"));
                            System.out.println("└──────────────────────────────────────\n");
                        }
                        
                        webSocket.request(1);
                        return null;
                    }
                    
                    @Override
                    public CompletableFuture<?> onClose(WebSocket webSocket, 
                                                         int statusCode, 
                                                         String reason) {
                        if (statusCode == WebSocket.NORMAL_CLOSURE) {
                            System.out.println("✓ WebSocket Closed Gracefully");
                        } else {
                            System.out.println("✗ WebSocket Closed: " + reason);
                        }
                        latch.countDown();
                        return null;
                    }
                    
                    @Override
                    public void onError(WebSocket webSocket, Throwable error) {
                        // Only log if it's not a normal shutdown
                        if (!(error instanceof java.nio.channels.ClosedChannelException)) {
                            System.err.println("✗ WebSocket Error: " + error.getMessage());
                        }
                    }
                    
                }).join();
        
        // Keep connection alive
        try {
            latch.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * Simple JSON field extraction (for demonstration only)
     */
    private String extractField(String json, String field) {
        String pattern = "\"" + field + "\":\"";
        int start = json.indexOf(pattern);
        if (start == -1) {
            pattern = "\"" + field + "\":";
            start = json.indexOf(pattern);
            if (start == -1) return field + ": N/A";
        }
        start += pattern.length();
        int end = json.indexOf(",", start);
        if (end == -1) end = json.indexOf("}", start);
        String value = json.substring(start, end).replace("\"", "").trim();
        return field + ": " + value;
    }
    
    /**
     * Display API response in formatted way (simple JSON parsing)
     */
    private void displayResponse(String jsonResponse, String title) {
        System.out.println("\n" + title);
        System.out.println("============================================================");
        
        // Extract success status
        boolean success = jsonResponse.contains("\"success\":true");
        
        if (!success) {
            // Extract error message
            String message = extractValue(jsonResponse, "message");
            System.out.println("❌ Error: " + message);
            return;
        }
        
        // Extract message and count
        String message = extractValue(jsonResponse, "message");
        String count = extractValue(jsonResponse, "count");
        
        System.out.println("✓ Success: " + message);
        System.out.println("Count: " + count + " QSOs");
        
        // Extract and display QSOs
        displayQSOs(jsonResponse);
        System.out.println();
    }
    
    /**
     * Extract a value from JSON (simple implementation)
     */
    private String extractValue(String json, String field) {
        String pattern = "\"" + field + "\":\"";
        int start = json.indexOf(pattern);
        if (start == -1) {
            pattern = "\"" + field + "\":";
            start = json.indexOf(pattern);
            if (start == -1) return "N/A";
        }
        start += pattern.length();
        int end = json.indexOf(",", start);
        if (end == -1) end = json.indexOf("}", start);
        if (end == -1) end = json.indexOf("]", start);
        String value = json.substring(start, end).replace("\"", "").trim();
        return value;
    }
    
    /**
     * Display QSO list from JSON (simple parsing)
     */
    private void displayQSOs(String jsonResponse) {
        // Find the qsos array
        int qsosStart = jsonResponse.indexOf("\"qsos\":[");
        if (qsosStart == -1) {
            // Try alternative format with space
            qsosStart = jsonResponse.indexOf("\"qsos\" :[");
            if (qsosStart == -1) {
                qsosStart = jsonResponse.indexOf("\"qsos\": [");
                if (qsosStart == -1) {
                    return;
                }
            }
        }
        
        // Find the actual start of the array content
        int arrayStart = jsonResponse.indexOf("[", qsosStart);
        if (arrayStart == -1) return;
        
        // Find the matching closing bracket
        int arrayEnd = findMatchingBracket(jsonResponse, arrayStart);
        if (arrayEnd == -1) return;
        
        String qsosArray = jsonResponse.substring(arrayStart + 1, arrayEnd);
        
        // Check if array is empty
        if (qsosArray.trim().isEmpty()) {
            return;
        }
        
        // Parse QSO objects
        java.util.List<String> qsoObjects = new java.util.ArrayList<>();
        int depth = 0;
        StringBuilder current = new StringBuilder();
        
        for (int i = 0; i < qsosArray.length(); i++) {
            char c = qsosArray.charAt(i);
            if (c == '{') {
                if (depth == 0) {
                    current = new StringBuilder();
                }
                depth++;
                current.append(c);
            } else if (c == '}') {
                current.append(c);
                depth--;
                if (depth == 0 && current.length() > 0) {
                    qsoObjects.add(current.toString());
                }
            } else if (depth > 0) {
                current.append(c);
            }
        }
        
        if (qsoObjects.isEmpty()) {
            return;
        }
        
        System.out.println("\nQSOs:");
        System.out.println("------------------------------------------------------------");
        
        int index = 1;
        for (String qsoJson : qsoObjects) {
            try {
                // Extract fields
                String callsign = extractFromFragment(qsoJson, "callsign");
                String dateOn = extractFromFragment(qsoJson, "dateOn");
                String timeOn = extractFromFragment(qsoJson, "timeOn");
                String mode = extractFromFragment(qsoJson, "mode");
                String frequency = extractFromFragment(qsoJson, "frequency");
                String rstSent = extractFromFragment(qsoJson, "rstSent");
                String rstRcvd = extractFromFragment(qsoJson, "rstReceived");
                
                // Pad/truncate fields for consistent display
                String paddedCallsign = String.format("%-10s", 
                    callsign.length() > 10 ? callsign.substring(0, 10) : callsign);
                String paddedMode = String.format("%-6s", 
                    mode.length() > 6 ? mode.substring(0, 6) : mode);
                
                // Format frequency with proper decimal places
                String formattedFreq = "N/A".equals(frequency) ? "    N/A" : 
                    String.format("%8.3f", Double.parseDouble(frequency));
                
                // Format output
                System.out.printf("%3d. %s @ %s %s - %s %s MHz - %s/%s%n",
                    index++, 
                    paddedCallsign,
                    dateOn, timeOn, 
                    paddedMode,
                    formattedFreq, rstSent, rstRcvd);
            } catch (Exception e) {
                // Skip malformed entries
                System.err.println("Warning: Could not parse QSO entry " + index);
            }
        }
    }
    
    /**
     * Find matching closing bracket for an opening bracket
     */
    private int findMatchingBracket(String json, int start) {
        int depth = 1;
        for (int i = start + 1; i < json.length(); i++) {
            char c = json.charAt(i);
            if (c == '[') depth++;
            else if (c == ']') {
                depth--;
                if (depth == 0) return i;
            }
        }
        return -1;
    }
    
    /**
     * Extract field value from JSON fragment
     */
    private String extractFromFragment(String fragment, String field) {
        String pattern = "\"" + field + "\":";
        int start = fragment.indexOf(pattern);
        if (start == -1) return "N/A";
        
        start += pattern.length();
        
        // Skip whitespace
        while (start < fragment.length() && Character.isWhitespace(fragment.charAt(start))) {
            start++;
        }
        
        // Check if it's a string value (starts with quote)
        if (start < fragment.length() && fragment.charAt(start) == '"') {
            start++; // Skip opening quote
            int end = fragment.indexOf("\"", start);
            if (end == -1) return "N/A";
            return fragment.substring(start, end);
        }
        
        // Check if it's an array (for dates like [2025,12,20])
        if (start < fragment.length() && fragment.charAt(start) == '[') {
            int end = fragment.indexOf("]", start);
            if (end == -1) return "N/A";
            String arrayStr = fragment.substring(start + 1, end);
            String[] parts = arrayStr.split(",");
            if (parts.length >= 3) {
                // Format as YYYY-MM-DD
                return String.format("%s-%02d-%02d", 
                    parts[0].trim(), 
                    Integer.parseInt(parts[1].trim()), 
                    Integer.parseInt(parts[2].trim()));
            }
            return arrayStr;
        }
        
        // It's a number or boolean
        int end = fragment.indexOf(",", start);
        if (end == -1) end = fragment.length();
        
        // Also check for } which ends an object
        int braceEnd = fragment.indexOf("}", start);
        if (braceEnd != -1 && braceEnd < end) {
            end = braceEnd;
        }
        
        String value = fragment.substring(start, end).trim();
        return value.isEmpty() ? "N/A" : value;
    }
    
    // ========== Main Example ==========
    
    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("Usage: java SimplePicondoClient <api_key>");
            System.exit(0);
        }

        SimplePicondoClient client = new SimplePicondoClient(args[0]);
        
        System.out.println("============================================================");
        System.out.println("  Simple Picondo API Client - Java");
        System.out.println("============================================================");
        System.out.println();
        
        try {
            // Example 1: Health check
            System.out.println("1. Checking API health...");
            String health = client.checkHealth();
            System.out.println("Health: " + health);
            System.out.println();
            
            // Example 2: REST API query
            System.out.println("2. Querying QSOs for callsign G4XYZ...");
            String result = client.queryByCallsign("G4XYZ");
            client.displayResponse(result, "Query by Callsign");
            
            // Example 3: Query by date
            System.out.println("3. Querying QSOs from last 7 days...");
            LocalDate endDate = LocalDate.now();
            LocalDate startDate = endDate.minusDays(7);
            result = client.queryByDate(startDate, endDate);
            client.displayResponse(result, "Query by Date Range");
            
            // Example 4: WebSocket listener
            System.out.println("4. Starting WebSocket listener...");
            System.out.println("(This will run until you press Ctrl+C)");
            System.out.println();
            client.connectWebSocket();
            
        } catch (Exception e) {
            System.err.println("\n❌ Error: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
