package com.cube.shell;

import com.cube.cluster.ClusterNode;
import com.cube.consistency.ConsistencyLevel;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.*;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

/**
 * CubeShell - Interactive SQL shell for Cube database with cluster management
 * 
 * Features:
 * - Connect to multiple cluster nodes
 * - View cluster topology
 * - Set consistency levels
 * - Execute queries with replication
 * - Monitor node health
 * - View replication statistics
 */
public class CubeShell {
    // 0x1000-table start. --16
    // 0x1100-table end.   --16+4
    //
    // 0x0001-right alignment  -- 1
    // 0x0010-left alignment.  == 2
    // 0x0011- center.     ==3

    
    private static final String VERSION = "2.0.0";
    private static final String PROMPT = "cube> ";
    private static final int FMT_TABLE = 16;
    private static final int FMT_TABLE_END = 4;
    private static final int FMT_RIGHT_ALIGN= 1;
    private static final int FMT_LEFT_ALIGN= 2;
    private static final int FMT_CENTER_ALIGN=FMT_LEFT_ALIGN|FMT_RIGHT_ALIGN;
    private static final int FMT_TABLE_SEPRATOR = 8;


    private static final String TABLE_BORDER_VERTICAL = "║";
    private static final String TABLE_START_LEFT="╔";
    private static final String TABLE_START_RIGHT="╗";


    private static final String TABLE_END_LEFT="╚";
    private static final String TABLE_END_RIGHT="╝";

    private static final int TABLE_MAX_LENGTH =100 ;
    private static final String TABLE_BORDER_HORIZANTAL = "═";
    private static final String TABLE_SEPRATOR_LEFT = "╠";
    private static final String TABLE_SEPRATOR_RIGHT = "╣";

    private final List<ClusterNode> clusterNodes;
    private ClusterNode currentNode;
    private ConsistencyLevel defaultConsistencyLevel;
    private final HttpClient httpClient;
    private final ObjectMapper objectMapper;
    private final List<String> commandHistory;
    private boolean running;
    
    public CubeShell() {
        this.clusterNodes = new ArrayList<>();
        this.defaultConsistencyLevel = ConsistencyLevel.ONE;
        this.httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(5))
            .build();
        this.objectMapper = new ObjectMapper();
        this.commandHistory = new ArrayList<>();
        this.running = true;
    }
    
    public static void main(String[] args) {
        CubeShell shell = new CubeShell();
        shell.printBanner();
        
        // Parse command line arguments
        String initialHost = "localhost";
        int initialPort = 8080;
        
        for (int i = 0; i < args.length; i++) {
            if (args[i].equals("--host") || args[i].equals("-h")) {
                if (i + 1 < args.length) {
                    initialHost = args[++i];
                }
            } else if (args[i].equals("--port") || args[i].equals("-p")) {
                if (i + 1 < args.length) {
                    initialPort = Integer.parseInt(args[++i]);
                }
            }
        }
        
        // Auto-connect to initial node
        shell.connectToNode(initialHost, initialPort);
        
        // Start interactive shell
        shell.run();
    }
    
    private void printBanner() {
        System.out.println( fillChar(TABLE_MAX_LENGTH, "═"));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "CubeShell v" + VERSION + " ", " ", FMT_CENTER_ALIGN |FMT_TABLE, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "CubeCactus Interactive Shell", " ", FMT_CENTER_ALIGN , true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "SQL/CQL + Cluster Management", " ", FMT_CENTER_ALIGN |FMT_TABLE| FMT_TABLE_END, true));
        System.out.println();
        System.out.println("Type 'HELP' for available commands, 'EXIT' to quit.");
        System.out.println();

    }

    private String fillFormat(int len, String text, String filler, int align, boolean table) {
        StringBuilder sb = new StringBuilder();

        //System.out.printf("align="+ align + "(align & FMT_TABLE_START)" + (align & FMT_TABLE_START) + "==" + FMT_TABLE_START );
        if (((align & FMT_TABLE) == FMT_TABLE) && (align & FMT_TABLE_END) != FMT_TABLE_END   &&((align & FMT_TABLE_SEPRATOR) != FMT_TABLE_SEPRATOR)) {
            int startLen=len;
            if (table) {
                startLen= startLen -2;
            }

            if (table) {
                sb.append(TABLE_START_LEFT);
            }

            sb.append(TABLE_BORDER_HORIZANTAL.repeat(startLen));
            if (table) {
                sb.append(TABLE_START_RIGHT);
            }
            sb.append("\n");
        }


        if ((((align & FMT_TABLE) == FMT_TABLE) && (align & FMT_TABLE_END) != FMT_TABLE_END) &&((align & FMT_TABLE_SEPRATOR) == FMT_TABLE_SEPRATOR))  {
            int startLen=len;
            if (table) {
                startLen= startLen -2;
            }

            if (table) {
                sb.append(TABLE_SEPRATOR_LEFT);
            }

            sb.append(TABLE_BORDER_HORIZANTAL.repeat(startLen));
            if (table) {
                sb.append(TABLE_SEPRATOR_RIGHT);
            }
            sb.append("\n");
        }

        if ((align & FMT_CENTER_ALIGN) == FMT_CENTER_ALIGN) {
           int textLen= text.length();
           int centerLen= len;
           if (table) {
               centerLen = centerLen -2;
           }
           if (textLen < centerLen) {
               int padLen= centerLen-textLen;
               int leftPadLen= padLen/2;
               int rightPadLen=centerLen- (leftPadLen +textLen);
               if (table) {
                   sb.append(TABLE_BORDER_VERTICAL);
               }

               sb.append(filler.repeat(leftPadLen));
               sb.append(text);
               sb.append(filler.repeat(rightPadLen));
               if (table) {
                   sb.append(TABLE_BORDER_VERTICAL);
               }
           } else {
               sb.append(text);
           }
        } else if ((align & FMT_LEFT_ALIGN) == FMT_LEFT_ALIGN) {
            int textLen= text.length();
            int leftLen= len;
            if (table) {
                leftLen = leftLen -2;
            }
            if (textLen < leftLen) {

                int rightPadLen=leftLen-textLen;
                if (table) {
                    sb.append(TABLE_BORDER_VERTICAL);
                }

                sb.append(text);
                sb.append(filler.repeat(rightPadLen));
                if (table) {
                    sb.append(TABLE_BORDER_VERTICAL);
                }
            } else {
                sb.append(text);
            }
        } else if ((align & FMT_RIGHT_ALIGN) == FMT_RIGHT_ALIGN) {
            int textLen= text.length();
            int rightLen= len;
            if (table) {
                rightLen = rightLen -2;
            }
            if (textLen < rightLen) {

                int leftPadLen=rightLen-textLen;
                if (table) {
                    sb.append(TABLE_BORDER_VERTICAL);
                }
                sb.append(filler.repeat(leftPadLen));
                sb.append(text);

                if (table) {
                    sb.append(TABLE_BORDER_VERTICAL);
                }
            } else {
                sb.append(text);
            }
        }

        if (((align & FMT_TABLE) == FMT_TABLE) && (align & FMT_TABLE_END) == FMT_TABLE_END) {
            sb.append("\n");
            int startLen=len;
            if (table) {
                startLen= startLen -2;
            }

            if (table) {
                sb.append(TABLE_END_LEFT);
            }

            sb.append(TABLE_BORDER_HORIZANTAL.repeat(startLen));
            if (table) {
                sb.append(TABLE_END_RIGHT);
            }
            sb.append("\n");
        }
        return  sb.toString();
        // return len/2 + text.length(),
    }

    private String fillChar(int len, String s) {
           return s.repeat(len);
    }

    private void run() {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
            while (running) {
                System.out.print(PROMPT);
                String line = reader.readLine();
                
                if (line == null) {
                    break;
                }
                
                line = line.trim();
                if (line.isEmpty()) {
                    continue;
                }
                
                commandHistory.add(line);
                processCommand(line);
            }
        } catch (IOException e) {
            System.err.println("Error reading input: " + e.getMessage());
        }
        
        System.out.println("\nGoodbye!");
    }
    
    private void processCommand(String line) {
        String[] parts = line.split("\\s+", 2);
        String command = parts[0].toUpperCase();
        String args = parts.length > 1 ? parts[1] : "";
        
        try {
            switch (command) {
                case "HELP":
                case "?":
                    showHelp();
                    break;
                    
                case "CONNECT":
                    handleConnect(args);
                    break;
                    
                case "DISCONNECT":
                    handleDisconnect(args);
                    break;
                    
                case "NODES":
                case "CLUSTER":
                    showClusterInfo();
                    break;
                    
                case "USE":
                    handleUseNode(args);
                    break;
                    
                case "CONSISTENCY":
                case "CL":
                    handleConsistency(args);
                    break;
                    
                case "STATUS":
                    showNodeStatus();
                    break;
                    
                case "STATS":
                case "STATISTICS":
                    showReplicationStats();
                    break;
                    
                // SQL/CQL Commands
                case "SELECT":
                case "INSERT":
                case "UPDATE":
                case "DELETE":
                case "CREATE":
                case "DROP":
                case "DESCRIBE":
                case "DESC":
                case "SHOW":
                    handleSQL(line);
                    break;
                    
                case "PUT":
                    handlePut(args);
                    break;
                    
                case "GET":
                    handleGet(args);
                    break;
                    
                case "DEL":
                    handleDelete(args);
                    break;
                    
                case "SCAN":
                    handleScan(args);
                    break;
                    
                case "HISTORY":
                    showHistory();
                    break;
                    
                case "CLEAR":
                case "CLS":
                    clearScreen();
                    break;
                    
                case "EXIT":
                case "QUIT":
                case "BYE":
                    running = false;
                    break;
                    
                default:
                    // If not a shell command, try to execute as SQL
                    if (line.contains(" ") || line.endsWith(";")) {
                        handleSQL(line);
                    } else {
                        System.out.println("Unknown command: " + command);
                        System.out.println("Type 'HELP' for available commands.");
                    }
            }
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
        }
    }
    
    private void showHelp() {
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "CubeShell Commands v" + VERSION + " ", " ", FMT_CENTER_ALIGN |FMT_TABLE, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "SQL/CQL Queries:" + VERSION + " ", " ", FMT_LEFT_ALIGN |FMT_TABLE|FMT_TABLE_SEPRATOR, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "  CREATE TABLE keyspace.table (col TYPE, ...) " + VERSION + " ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "  INSERT INTO table (cols) VALUES (vals) " + VERSION + " ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    INSERT INTO table (cols) VALUES (vals)               " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    SELECT * FROM table WHERE id = 'value'               " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    UPDATE table SET col='val' WHERE id='value'          " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    DELETE FROM table WHERE id='value'                   " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    DESCRIBE table                                       " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    SHOW TABLES                                          " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "                                                         " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "  Examples:                                              " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    CREATE TABLE users (id TEXT PRIMARY KEY, name TEXT)  " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    INSERT INTO users (id, name) VALUES ('1', 'Alice')   " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    SELECT * FROM users WHERE id = '1'                   " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    UPDATE users SET name = 'Bob' WHERE id = '1'         " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "                                                         " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "  Key-Value Operations:                                  " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    PUT <key> <value>         - Store key-value pair     " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    GET <key>                 - Retrieve value           " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    DEL <key>                 - Delete key               " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    SCAN <prefix>             - Scan keys by prefix      " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "                                                         " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "  Cluster Management:                                    " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    CONNECT <host> <port>     - Connect to node          " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    DISCONNECT <host> <port>  - Disconnect from node     " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    NODES / CLUSTER           - Show cluster topology    " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    USE <index>               - Switch active node       " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    STATUS                    - Show current node status " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    STATS                     - Show replication stats   " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "                                                         " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "  Consistency:                                           " , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    CONSISTENCY <level>       - Set consistency  (ONE/QUORUM/ALL)" , " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    CL <level>                - Short form               ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "                                                         ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "  Utility:                                               ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    HISTORY                   - Show command history     ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    CLEAR / CLS               - Clear screen             ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    HELP / ?                  - Show this help           ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    EXIT / QUIT               - Exit shell               ", " ", FMT_LEFT_ALIGN|FMT_TABLE|FMT_TABLE_SEPRATOR, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "                                                         ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    SELECT * FROM table WHERE id = 'value'               ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    UPDATE table SET col='val' WHERE id='value'          ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    DELETE FROM table WHERE id='value'                   ", " ", FMT_LEFT_ALIGN, true));
        System.out.println( fillFormat(TABLE_MAX_LENGTH, "    DESCRIBE table                                       ", " ", FMT_LEFT_ALIGN, true));

        System.out.println("Examples:");
        System.out.println("  CREATE TABLE users.profiles (id TEXT PRIMARY KEY, name TEXT, email TEXT)");
        System.out.println("  INSERT INTO users.profiles (id, name, email) VALUES ('1', 'Alice', 'alice@example.com')");
        System.out.println("  SELECT * FROM users.profiles WHERE id = '1'");
        System.out.println("  UPDATE users.profiles SET name = 'Alice Johnson' WHERE id = '1'");
        System.out.println("  DELETE FROM users.profiles WHERE id = '1'");
        System.out.println();
    }
    
    private void handleConnect(String args) {
        String[] parts = args.split("\\s+");
        if (parts.length < 2) {
            System.out.println("Usage: CONNECT <host> <port>");
            return;
        }
        
        String host = parts[0];
        int port = Integer.parseInt(parts[1]);
        
        connectToNode(host, port);
    }
    
    private void connectToNode(String host, int port) {
        try {
            // Test connection
            String url = String.format("http://%s:%d/api/v1/health", host, port);
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .timeout(Duration.ofSeconds(5))
                .GET()
                .build();
            
            HttpResponse<String> response = httpClient.send(request, 
                HttpResponse.BodyHandlers.ofString());
            
            if (response.statusCode() == 200) {
                // Extract node ID from response or generate one
                String nodeId = String.format("node-%s-%d", host, port);
                
                ClusterNode node = new ClusterNode(nodeId, host, port);
                
                // Check if already connected
                boolean exists = clusterNodes.stream()
                    .anyMatch(n -> n.getHost().equals(host) && n.getPort() == port);
                
                if (!exists) {
                    clusterNodes.add(node);
                    System.out.println("✓ Connected to " + host + ":" + port);
                    System.out.println("  Node ID: " + nodeId);
                    
                    if (currentNode == null) {
                        currentNode = node;
                        System.out.println("  Set as current node");
                    }
                } else {
                    System.out.println("Already connected to " + host + ":" + port);
                }
            } else {
                System.out.println("✗ Failed to connect: HTTP " + response.statusCode());
            }
        } catch (Exception e) {
            System.out.println("✗ Failed to connect: " + e.getMessage());
        }
    }
    
    private void handleDisconnect(String args) {
        if (args.isEmpty()) {
            System.out.println("Usage: DISCONNECT <node-id>");
            return;
        }
        
        String nodeId = args.trim();
        boolean removed = clusterNodes.removeIf(n -> n.getNodeId().equals(nodeId));
        
        if (removed) {
            System.out.println("✓ Disconnected from " + nodeId);
            
            if (currentNode != null && currentNode.getNodeId().equals(nodeId)) {
                currentNode = clusterNodes.isEmpty() ? null : clusterNodes.get(0);
                if (currentNode != null) {
                    System.out.println("  Switched to " + currentNode.getNodeId());
                }
            }
        } else {
            System.out.println("✗ Node not found: " + nodeId);
        }
    }
    
    private void showClusterInfo() {
        System.out.println("\n╔════════════════════════════════════════════════════════════╗");
        System.out.println("║                    Cluster Nodes                           ║");
        System.out.println("╠════════════════════════════════════════════════════════════╣");
        
        if (clusterNodes.isEmpty()) {
            System.out.println("║  No nodes connected                                        ║");
            System.out.println("╚════════════════════════════════════════════════════════════╝\n");
            return;
        }
        
        for (ClusterNode node : clusterNodes) {
            String current = (node.equals(currentNode)) ? "➜ " : "  ";
            String status = node.isAlive() ? "✓" : "✗";
            
            System.out.printf("║ %s%s %-20s %s %-25s ║%n", 
                current, status, node.getNodeId(), 
                node.getEndpoint(),
                "DC:" + node.getDatacenter());
        }
        
        System.out.println("╠════════════════════════════════════════════════════════════╣");
        System.out.printf("║ Total Nodes: %-3d  Alive: %-3d  Current: %-18s║%n",
            clusterNodes.size(),
            clusterNodes.stream().filter(ClusterNode::isAlive).count(),
            currentNode != null ? currentNode.getNodeId() : "none");
        System.out.println("╚════════════════════════════════════════════════════════════╝\n");
    }
    
    private void handleUseNode(String args) {
        if (args.isEmpty()) {
            System.out.println("Usage: USE <node-id>");
            return;
        }
        
        String nodeId = args.trim();
        ClusterNode node = clusterNodes.stream()
            .filter(n -> n.getNodeId().equals(nodeId))
            .findFirst()
            .orElse(null);
        
        if (node != null) {
            currentNode = node;
            System.out.println("✓ Switched to " + nodeId);
        } else {
            System.out.println("✗ Node not found: " + nodeId);
        }
    }
    
    private void handleConsistency(String args) {
        if (args.isEmpty()) {
            System.out.println("\nCurrent consistency level: " + defaultConsistencyLevel);
            System.out.println("\nAvailable levels:");
            for (ConsistencyLevel cl : ConsistencyLevel.values()) {
                System.out.println("  " + cl.name() + " - " + cl.getDescription());
            }
            System.out.println();
            return;
        }
        
        try {
            ConsistencyLevel cl = ConsistencyLevel.valueOf(args.trim().toUpperCase());
            defaultConsistencyLevel = cl;
            System.out.println("✓ Consistency level set to " + cl);
        } catch (IllegalArgumentException e) {
            System.out.println("✗ Invalid consistency level: " + args);
            System.out.println("  Valid: ONE, TWO, THREE, QUORUM, ALL, ANY");
        }
    }
    
    private void showNodeStatus() {
        if (currentNode == null) {
            System.out.println("✗ Not connected to any node");
            return;
        }
        
        try {
            String url = String.format("http://%s:%d/api/v1/stats",
                currentNode.getHost(), currentNode.getPort());
            
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .GET()
                .build();
            
            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());
            
            if (response.statusCode() == 200) {
                @SuppressWarnings("unchecked")
                Map<String, Object> data = objectMapper.readValue(
                    response.body(), Map.class);
                
                System.out.println("\n╔════════════════════════════════════════════════════════════╗");
                System.out.println("║                    Node Status                             ║");
                System.out.println("╠════════════════════════════════════════════════════════════╣");
                System.out.printf("║ Node:        %-45s ║%n", currentNode.getNodeId());
                System.out.printf("║ Endpoint:    %-45s ║%n", currentNode.getEndpoint());
                System.out.printf("║ Status:      %-45s ║%n", "✓ ALIVE");
                
                if (data.containsKey("stats")) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> stats = (Map<String, Object>) data.get("stats");
                    
                    System.out.println("╠════════════════════════════════════════════════════════════╣");
                    System.out.println("║ Storage Statistics:                                        ║");
                    System.out.printf("║   Total Keys:     %-40s ║%n", stats.get("totalKeys"));
                    System.out.printf("║   Total Size:     %-40s ║%n", stats.get("totalSize") + " bytes");
                    System.out.printf("║   MemTable Size:  %-40s ║%n", stats.get("memtableSize") + " bytes");
                    System.out.printf("║   SSTable Count:  %-40s ║%n", stats.get("sstableCount"));
                }
                
                System.out.println("╚════════════════════════════════════════════════════════════╝\n");
            }
        } catch (Exception e) {
            System.out.println("✗ Failed to get status: " + e.getMessage());
        }
    }
    
    private void showReplicationStats() {
        System.out.println("\n╔════════════════════════════════════════════════════════════╗");
        System.out.println("║              Replication Statistics                        ║");
        System.out.println("╠════════════════════════════════════════════════════════════╣");
        System.out.printf("║ Cluster Nodes:           %-33d ║%n", clusterNodes.size());
        System.out.printf("║ Alive Nodes:             %-33d ║%n", 
            clusterNodes.stream().filter(ClusterNode::isAlive).count());
        System.out.printf("║ Default Consistency:     %-33s ║%n", defaultConsistencyLevel);
        System.out.println("╠════════════════════════════════════════════════════════════╣");
        System.out.println("║ Datacenter Distribution:                                   ║");
        
        Map<String, Long> dcCount = clusterNodes.stream()
            .collect(Collectors.groupingBy(ClusterNode::getDatacenter, Collectors.counting()));
        
        for (Map.Entry<String, Long> entry : dcCount.entrySet()) {
            System.out.printf("║   %-20s %-33d ║%n", entry.getKey() + ":", entry.getValue());
        }
        
        System.out.println("╚════════════════════════════════════════════════════════════╝\n");
    }
    
    private void handlePut(String args) {
        if (currentNode == null) {
            System.out.println("✗ Not connected to any node");
            return;
        }
        
        String[] parts = args.split("\\s+", 2);
        if (parts.length < 2) {
            System.out.println("Usage: PUT <key> <value>");
            return;
        }
        
        String key = parts[0];
        String value = parts[1];
        
        try {
            String url = String.format("http://%s:%d/api/v1/put",
                currentNode.getHost(), currentNode.getPort());
            
            Map<String, String> body = new HashMap<>();
            body.put("key", key);
            body.put("value", value);
            
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(
                    objectMapper.writeValueAsString(body)))
                .build();
            
            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());
            
            if (response.statusCode() == 200) {
                System.out.println("✓ PUT successful");
                System.out.println("  Key: " + key);
                System.out.println("  Value: " + value);
                System.out.println("  CL: " + defaultConsistencyLevel);
            } else {
                System.out.println("✗ PUT failed: HTTP " + response.statusCode());
            }
        } catch (Exception e) {
            System.out.println("✗ Error: " + e.getMessage());
        }
    }
    
    private void handleGet(String args) {
        if (currentNode == null) {
            System.out.println("✗ Not connected to any node");
            return;
        }
        
        if (args.isEmpty()) {
            System.out.println("Usage: GET <key>");
            return;
        }
        
        String key = args.trim();
        
        try {
            String url = String.format("http://%s:%d/api/v1/get/%s",
                currentNode.getHost(), currentNode.getPort(), key);
            
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .GET()
                .build();
            
            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());
            
            if (response.statusCode() == 200) {
                @SuppressWarnings("unchecked")
                Map<String, Object> data = objectMapper.readValue(
                    response.body(), Map.class);
                
                if (Boolean.TRUE.equals(data.get("found"))) {
                    System.out.println("✓ Found");
                    System.out.println("  Key: " + key);
                    System.out.println("  Value: " + data.get("value"));
                    System.out.println("  CL: " + defaultConsistencyLevel);
                } else {
                    System.out.println("✗ Not found: " + key);
                }
            } else {
                System.out.println("✗ GET failed: HTTP " + response.statusCode());
            }
        } catch (Exception e) {
            System.out.println("✗ Error: " + e.getMessage());
        }
    }
    
    private void handleDelete(String args) {
        if (currentNode == null) {
            System.out.println("✗ Not connected to any node");
            return;
        }
        
        if (args.isEmpty()) {
            System.out.println("Usage: DELETE <key>");
            return;
        }
        
        String key = args.trim();
        
        try {
            String url = String.format("http://%s:%d/api/v1/delete/%s",
                currentNode.getHost(), currentNode.getPort(), key);
            
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .DELETE()
                .build();
            
            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());
            
            if (response.statusCode() == 200) {
                System.out.println("✓ DELETE successful");
                System.out.println("  Key: " + key);
                System.out.println("  CL: " + defaultConsistencyLevel);
            } else {
                System.out.println("✗ DELETE failed: HTTP " + response.statusCode());
            }
        } catch (Exception e) {
            System.out.println("✗ Error: " + e.getMessage());
        }
    }
    
    private void handleScan(String args) {
        if (currentNode == null) {
            System.out.println("✗ Not connected to any node");
            return;
        }
        
        if (args.isEmpty()) {
            System.out.println("Usage: SCAN <prefix>");
            return;
        }
        
        String prefix = args.trim();
        
        try {
            String url = String.format("http://%s:%d/api/v1/scan?prefix=%s",
                currentNode.getHost(), currentNode.getPort(), prefix);
            
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .GET()
                .build();
            
            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());
            
            if (response.statusCode() == 200) {
                @SuppressWarnings("unchecked")
                Map<String, Object> data = objectMapper.readValue(
                    response.body(), Map.class);
                
                int count = (int) data.get("count");
                System.out.println("✓ Found " + count + " result(s)");
                
                if (count > 0) {
                    @SuppressWarnings("unchecked")
                    Map<String, String> results = (Map<String, String>) data.get("results");
                    
                    System.out.println("\n┌────────────────────────────┬────────────────────────────┐");
                    System.out.println("│ Key                        │ Value                      │");
                    System.out.println("├────────────────────────────┼────────────────────────────┤");
                    
                    for (Map.Entry<String, String> entry : results.entrySet()) {
                        System.out.printf("│ %-26s │ %-26s │%n",
                            truncate(entry.getKey(), 26),
                            truncate(entry.getValue(), 26));
                    }
                    
                    System.out.println("└────────────────────────────┴────────────────────────────┘");
                }
                System.out.println();
            } else {
                System.out.println("✗ SCAN failed: HTTP " + response.statusCode());
            }
        } catch (Exception e) {
            System.out.println("✗ Error: " + e.getMessage());
        }
    }
    
    /**
     * Handle SQL/CQL query execution
     */
    private void handleSQLV1(String sql) {
        if (currentNode == null) {
            System.out.println("✗ Not connected to any node");
            return;
        }
        
        // Remove trailing semicolon if present
        sql = sql.trim();
        if (sql.endsWith(";")) {
            sql = sql.substring(0, sql.length() - 1).trim();
        }
        
        try {
            String url = String.format("http://%s:%d/api/v1/sql/execute",
                currentNode.getHost(), currentNode.getPort());
            
            Map<String, String> requestBody = new HashMap<>();
            requestBody.put("sql", sql);
            
            String jsonBody = objectMapper.writeValueAsString(requestBody);
            
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
                .build();
            
            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());
            
            if (response.statusCode() == 200) {
                @SuppressWarnings("unchecked")
                Map<String, Object> data = objectMapper.readValue(
                    response.body(), Map.class);
                
                boolean success = (boolean) data.getOrDefault("success", false);
                String message = (String) data.getOrDefault("message", "");
                
                if (success) {
                    System.out.println("✓ " + message);
                    
                    // Check if there are rows to display
                    if (data.containsKey("rows")) {
                        @SuppressWarnings("unchecked")
                        List<Map<String, String>> rows = 
                            (List<Map<String, String>>) data.get("rows");
                        
                        if (!rows.isEmpty()) {
                            displayResultTable(rows);
                        }
                    }
                    
                    // Display rows affected if present
                    if (data.containsKey("rowsAffected")) {
                        int rowsAffected = (int) data.get("rowsAffected");
                        System.out.println("  (" + rowsAffected + " row(s) affected)");
                    }
                    
                    // Display row count if present
                    if (data.containsKey("rowCount")) {
                        int rowCount = (int) data.get("rowCount");
                        if (rowCount > 0) {
                            System.out.println("  Found " + rowCount + " row(s)");
                        }
                    }
                } else {
                    System.out.println("✗ " + message);
                }
            } else {
                System.out.println("✗ SQL execution failed: HTTP " + response.statusCode());
            }
            
        } catch (Exception e) {
            System.out.println("✗ Error: " + e.getMessage());
        }
    }
    
    /**
     * Display SQL results as a formatted table
     */
    private void displayResultTable(List<Map<String, String>> rows) {
        if (rows.isEmpty()) {
            return;
        }
        
        // Get all column names
        Set<String> allColumns = new LinkedHashSet<>();
        for (Map<String, String> row : rows) {
            allColumns.addAll(row.keySet());
        }
        
        List<String> columns = new ArrayList<>(allColumns);
        int numColumns = columns.size();
        
        // Calculate column widths (max 30 chars per column)
        Map<String, Integer> colWidths = new HashMap<>();
        for (String col : columns) {
            int maxWidth = col.length();
            for (Map<String, String> row : rows) {
                String val = row.getOrDefault(col, "");
                maxWidth = Math.max(maxWidth, Math.min(val.length(), 30));
            }
            colWidths.put(col, Math.min(maxWidth + 2, 30));
        }
        
        // Print table
        System.out.println();
        
        // Top border
        printTableBorder(columns, colWidths, "┌", "┬", "┐");
        
        // Header
        System.out.print("│");
        for (String col : columns) {
            int width = colWidths.get(col);
            System.out.print(" " + padRight(col, width - 1) + "│");
        }
        System.out.println();
        
        // Header separator
        printTableBorder(columns, colWidths, "├", "┼", "┤");
        
        // Data rows
        for (Map<String, String> row : rows) {
            System.out.print("│");
            for (String col : columns) {
                int width = colWidths.get(col);
                String val = row.getOrDefault(col, "");
                System.out.print(" " + padRight(truncate(val, 28), width - 1) + "│");
            }
            System.out.println();
        }
        
        // Bottom border
        printTableBorder(columns, colWidths, "└", "┴", "┘");
        System.out.println();
    }
    
    /**
     * Print table border
     */
    private void printTableBorder(List<String> columns, Map<String, Integer> colWidths,
                                  String left, String mid, String right) {
        System.out.print(left);
        for (int i = 0; i < columns.size(); i++) {
            String col = columns.get(i);
            int width = colWidths.get(col);
            System.out.print("─".repeat(width));
            if (i < columns.size() - 1) {
                System.out.print(mid);
            }
        }
        System.out.println(right);
    }
    
    /**
     * Pad string to the right with spaces
     */
    private String padRight(String str, int length) {
        if (str.length() >= length) {
            return str;
        }
        return str + " ".repeat(length - str.length());
    }
    
    private void handleSQL(String sql) {
        if (currentNode == null) {
            System.out.println("✗ Not connected to any node");
            return;
        }
        
        try {
            String url = String.format("http://%s:%d/api/v1/sql/execute",
                currentNode.getHost(), currentNode.getPort());
            
            Map<String, String> body = new HashMap<>();
            body.put("sql", sql);
            
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(
                    objectMapper.writeValueAsString(body)))
                .timeout(Duration.ofSeconds(30))
                .build();
            
            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());
            
            if (response.statusCode() == 200) {
                @SuppressWarnings("unchecked")
                Map<String, Object> data = objectMapper.readValue(
                    response.body(), Map.class);
                
                boolean success = Boolean.TRUE.equals(data.get("success"));
                
                if (success) {
                    System.out.println("✓ Query executed successfully");
                    
                    // Display message if present
                    if (data.containsKey("message")) {
                        System.out.println("  " + data.get("message"));
                    }
                    
                    // Display rows affected if present
                    if (data.containsKey("rowsAffected")) {
                        System.out.println("  Rows affected: " + data.get("rowsAffected"));
                    }
                    
                    // Display result rows if present
                    if (data.containsKey("rows")) {
                        @SuppressWarnings("unchecked")
                        List<Map<String, String>> rows = (List<Map<String, String>>) data.get("rows");
                        
                        if (!rows.isEmpty()) {
                            displayResultTable(rows);
                        }
                    }
                    
                    // Display row count if present
                    if (data.containsKey("rowCount")) {
                        System.out.println("  Row count: " + data.get("rowCount"));
                    }
                    
                    System.out.println("  CL: " + defaultConsistencyLevel);
                } else {
                    System.out.println("✗ Query failed");
                    if (data.containsKey("message")) {
                        System.out.println("  Error: " + data.get("message"));
                    }
                }
            } else {
                System.out.println("✗ Query failed: HTTP " + response.statusCode());
                System.out.println("  Response: " + response.body());
            }
        } catch (Exception e) {
            System.out.println("✗ Error executing SQL: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    private void displayResultTableV1(List<Map<String, String>> rows) {
        if (rows.isEmpty()) {
            System.out.println("  (Empty result set)");
            return;
        }
        
        // Get all unique column names
        Set<String> columnSet = new LinkedHashSet<>();
        for (Map<String, String> row : rows) {
            columnSet.addAll(row.keySet());
        }
        List<String> columns = new ArrayList<>(columnSet);
        
        // Calculate column widths
        Map<String, Integer> widths = new HashMap<>();
        for (String col : columns) {
            int maxWidth = col.length();
            for (Map<String, String> row : rows) {
                String value = row.get(col);
                if (value != null) {
                    maxWidth = Math.max(maxWidth, value.length());
                }
            }
            widths.put(col, Math.min(maxWidth, 30)); // Max 30 chars per column
        }
        
        // Print header
        System.out.println();
        printTableSeparator(columns, widths, '┌', '┬', '┐');
        printTableRow(columns, columns, widths);
        printTableSeparator(columns, widths, '├', '┼', '┤');
        
        // Print rows
        for (Map<String, String> row : rows) {
            List<String> values = new ArrayList<>();
            for (String col : columns) {
                String value = row.get(col);
                values.add(value != null ? value : "NULL");
            }
            printTableRow(columns, values, widths);
        }
        
        // Print footer
        printTableSeparator(columns, widths, '└', '┴', '┘');
        System.out.println();
    }
    
    private void printTableSeparator(List<String> columns, Map<String, Integer> widths, 
                                     char left, char middle, char right) {
        System.out.print(left);
        for (int i = 0; i < columns.size(); i++) {
            String col = columns.get(i);
            int width = widths.get(col);
            for (int j = 0; j < width + 2; j++) {
                System.out.print('─');
            }
            if (i < columns.size() - 1) {
                System.out.print(middle);
            }
        }
        System.out.println(right);
    }
    
    private void printTableRow(List<String> columns, List<String> values, 
                               Map<String, Integer> widths) {
        System.out.print("│");
        for (int i = 0; i < columns.size(); i++) {
            String col = columns.get(i);
            String value = values.get(i);
            int width = widths.get(col);
            
            if (value.length() > width) {
                value = value.substring(0, width - 3) + "...";
            }
            
            System.out.print(" " + String.format("%-" + width + "s", value) + " │");
        }
        System.out.println();
    }
    
    private void showHistory() {
        System.out.println("\n╔════════════════════════════════════════════════════════════╗");
        System.out.println("║                   Command History                          ║");
        System.out.println("╠════════════════════════════════════════════════════════════╣");
        
        if (commandHistory.isEmpty()) {
            System.out.println("║  No commands in history                                    ║");
        } else {
            int start = Math.max(0, commandHistory.size() - 20);
            for (int i = start; i < commandHistory.size(); i++) {
                System.out.printf("║ %3d: %-53s ║%n", i + 1, 
                    truncate(commandHistory.get(i), 53));
            }
        }
        
        System.out.println("╚════════════════════════════════════════════════════════════╝\n");
    }
    
    private void clearScreen() {
        System.out.print("\033[H\033[2J");
        System.out.flush();
        printBanner();
    }
    
    private String truncate(String str, int maxLen) {
        if (str.length() <= maxLen) {
            return str;
        }
        return str.substring(0, maxLen - 3) + "...";
    }
}
