import sys
import argparse
import shlex
from protocol import IcingProtocol

RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
BLUE = "\033[94m"
MAGENTA = "\033[95m"
CYAN = "\033[96m"
RESET = "\033[0m"

def print_help():
    """Display all available commands."""
    print(f"\n{YELLOW}=== Available Commands ==={RESET}")
    print(f"\n{CYAN}Basic Protocol Commands:{RESET}")
    print("  help                       - Show this help message")
    print("  peer_id <hex_pubkey>       - Set peer identity public key")
    print("  connect <port>             - Connect to a peer at the specified port")
    print("  show_state                 - Display current protocol state")
    print("  exit                       - Exit the program")
    
    print(f"\n{CYAN}Manual Protocol Operation:{RESET}")
    print("  generate_ephemeral_keys    - Generate ephemeral ECDH keys")
    print("  send_ping [cipher]         - Send PING request (cipher: 0=AES-GCM, 1=ChaCha20-Poly1305, default: 0)")
    print("  respond_ping <index> <0|1> - Respond to a PING (0=reject, 1=accept)")
    print("  send_handshake             - Send handshake with ephemeral keys")
    print("  generate_ecdhe <index>     - Process handshake at specified index")
    print("  derive_hkdf                - Derive encryption key using HKDF")
    print("  send_encrypted <plaintext> - Encrypt and send a message")
    print("  decrypt <index>            - Decrypt received message at index")
    
    print(f"\n{CYAN}Automatic Mode Commands:{RESET}")
    print("  auto start                 - Start automatic mode")
    print("  auto stop                  - Stop automatic mode")
    print("  auto status                - Show current auto mode status and configuration")
    print("  auto config <param> <value> - Configure auto mode parameters")
    print("  auto config list           - Show all configurable parameters")
    print("  auto message <text>        - Queue message for automatic sending")
    print("  auto passive               - Configure as passive peer (responds to pings but doesn't initiate)")
    print("  auto active                - Configure as active peer (initiates protocol)")
    print("  auto log                   - Toggle detailed logging for auto mode")
    
    print(f"\n{CYAN}Debugging Commands:{RESET}")
    print("  debug_message <index>      - Display detailed information about a message in the queue")
    
    print(f"\n{CYAN}Legacy Commands:{RESET}")
    print("  auto_responder <on|off>    - Enable/disable legacy auto responder (deprecated)")


def main():
    protocol = IcingProtocol()

    print(f"{YELLOW}\n======================================")
    print("   Icing Protocol - Secure Communication   ")
    print("======================================\n" + RESET)
    print(f"Listening on port: {protocol.local_port}")
    print(f"Your identity public key (hex): {protocol.identity_pubkey.hex()}")
    print_help()

    while True:
        try:
            line = input(f"{MAGENTA}Cmd>{RESET} ").strip()
        except EOFError:
            break
        if not line:
            continue
            
        parts = shlex.split(line)  # Handle quoted arguments properly
        cmd = parts[0].lower()
        
        try:
            # Basic commands
            if cmd == "exit":
                protocol.stop()
                break
                
            elif cmd == "help":
                print_help()
                
            elif cmd == "show_state":
                protocol.show_state()
                
            elif cmd == "peer_id":
                if len(parts) != 2:
                    print(f"{RED}[ERROR]{RESET} Usage: peer_id <hex_pubkey>")
                    continue
                try:
                    protocol.set_peer_identity(parts[1])
                except ValueError as e:
                    print(f"{RED}[ERROR]{RESET} Invalid public key: {e}")
                    
            elif cmd == "connect":
                if len(parts) != 2:
                    print(f"{RED}[ERROR]{RESET} Usage: connect <port>")
                    continue
                try:
                    port = int(parts[1])
                    protocol.connect_to_peer(port)
                except ValueError:
                    print(f"{RED}[ERROR]{RESET} Invalid port number.")
                except Exception as e:
                    print(f"{RED}[ERROR]{RESET} Connection failed: {e}")
            
            # Manual protocol operation
            elif cmd == "generate_ephemeral_keys":
                protocol.generate_ephemeral_keys()
                
            elif cmd == "send_ping":
                # Optional cipher parameter (0 = AES-GCM, 1 = ChaCha20-Poly1305)
                cipher = 0  # Default to AES-GCM
                if len(parts) >= 2:
                    try:
                        cipher = int(parts[1])
                        if cipher not in (0, 1):
                            print(f"{YELLOW}[WARNING]{RESET} Unsupported cipher code {cipher}. Using AES-GCM (0).")
                            cipher = 0
                    except ValueError:
                        print(f"{YELLOW}[WARNING]{RESET} Invalid cipher code. Using AES-GCM (0).")
                protocol.send_ping_request(cipher)
                
            elif cmd == "send_handshake":
                protocol.send_handshake()
                
            elif cmd == "respond_ping":
                if len(parts) != 3:
                    print(f"{RED}[ERROR]{RESET} Usage: respond_ping <index> <0|1>")
                    continue
                try:
                    idx = int(parts[1])
                    answer = int(parts[2])
                    if answer not in (0, 1):
                        print(f"{RED}[ERROR]{RESET} Answer must be 0 (reject) or 1 (accept).")
                        continue
                    protocol.respond_to_ping(idx, answer)
                except ValueError:
                    print(f"{RED}[ERROR]{RESET} Index and answer must be integers.")
                except Exception as e:
                    print(f"{RED}[ERROR]{RESET} Failed to respond to ping: {e}")
                    
            elif cmd == "generate_ecdhe":
                if len(parts) != 2:
                    print(f"{RED}[ERROR]{RESET} Usage: generate_ecdhe <index>")
                    continue
                try:
                    idx = int(parts[1])
                    protocol.generate_ecdhe(idx)
                except ValueError:
                    print(f"{RED}[ERROR]{RESET} Index must be an integer.")
                except Exception as e:
                    print(f"{RED}[ERROR]{RESET} Failed to process handshake: {e}")
                    
            elif cmd == "derive_hkdf":
                try:
                    protocol.derive_hkdf()
                except Exception as e:
                    print(f"{RED}[ERROR]{RESET} Failed to derive HKDF key: {e}")
                    
            elif cmd == "send_encrypted":
                if len(parts) < 2:
                    print(f"{RED}[ERROR]{RESET} Usage: send_encrypted <plaintext>")
                    continue
                plaintext = " ".join(parts[1:])
                try:
                    protocol.send_encrypted_message(plaintext)
                except Exception as e:
                    print(f"{RED}[ERROR]{RESET} Failed to send encrypted message: {e}")
                    
            elif cmd == "decrypt":
                if len(parts) != 2:
                    print(f"{RED}[ERROR]{RESET} Usage: decrypt <index>")
                    continue
                try:
                    idx = int(parts[1])
                    protocol.decrypt_received_message(idx)
                except ValueError:
                    print(f"{RED}[ERROR]{RESET} Index must be an integer.")
                except Exception as e:
                    print(f"{RED}[ERROR]{RESET} Failed to decrypt message: {e}")
                    
            # Debugging commands
            elif cmd == "debug_message":
                if len(parts) != 2:
                    print(f"{RED}[ERROR]{RESET} Usage: debug_message <index>")
                    continue
                try:
                    idx = int(parts[1])
                    protocol.debug_message(idx)
                except ValueError:
                    print(f"{RED}[ERROR]{RESET} Index must be an integer.")
                except Exception as e:
                    print(f"{RED}[ERROR]{RESET} Failed to debug message: {e}")
            
            # Automatic mode commands
            elif cmd == "auto":
                if len(parts) < 2:
                    print(f"{RED}[ERROR]{RESET} Usage: auto <command> [options]")
                    print("Available commands: start, stop, status, config, message, passive, active")
                    continue
                
                subcmd = parts[1].lower()
                
                if subcmd == "start":
                    protocol.start_auto_mode()
                    print(f"{GREEN}[AUTO]{RESET} Automatic mode started")
                
                elif subcmd == "stop":
                    protocol.stop_auto_mode()
                    print(f"{GREEN}[AUTO]{RESET} Automatic mode stopped")
                
                elif subcmd == "status":
                    config = protocol.get_auto_mode_config()
                    print(f"{YELLOW}=== Auto Mode Status ==={RESET}")
                    print(f"Active: {protocol.auto_mode.active}")
                    print(f"State: {protocol.auto_mode.state}")
                    print(f"\n{YELLOW}--- Configuration ---{RESET}")
                    for key, value in vars(config).items():
                        print(f"  {key}: {value}")
                
                elif subcmd == "config":
                    if len(parts) < 3:
                        print(f"{RED}[ERROR]{RESET} Usage: auto config <param> <value> or auto config list")
                        continue
                        
                    if parts[2].lower() == "list":
                        config = protocol.get_auto_mode_config()
                        print(f"{YELLOW}=== Auto Mode Configuration Parameters ==={RESET}")
                        for key, value in vars(config).items():
                            print(f"  {key} ({type(value).__name__}): {value}")
                        continue
                    
                    if len(parts) != 4:
                        print(f"{RED}[ERROR]{RESET} Usage: auto config <param> <value>")
                        continue
                        
                    param = parts[2]
                    value_str = parts[3]
                    
                    # Convert the string value to the appropriate type
                    config = protocol.get_auto_mode_config()
                    if not hasattr(config, param):
                        print(f"{RED}[ERROR]{RESET} Unknown parameter: {param}")
                        print("Use 'auto config list' to see all available parameters")
                        continue
                        
                    current_value = getattr(config, param)
                    try:
                        if isinstance(current_value, bool):
                            if value_str.lower() in ("true", "yes", "on", "1"):
                                value = True
                            elif value_str.lower() in ("false", "no", "off", "0"):
                                value = False
                            else:
                                raise ValueError(f"Boolean value must be true/false/yes/no/on/off/1/0")
                        elif isinstance(current_value, int):
                            value = int(value_str)
                        elif isinstance(current_value, float):
                            value = float(value_str)
                        elif isinstance(current_value, str):
                            value = value_str
                        else:
                            value = value_str  # Default to string
                            
                        protocol.configure_auto_mode(**{param: value})
                        print(f"{GREEN}[AUTO]{RESET} Set {param} = {value}")
                    
                    except ValueError as e:
                        print(f"{RED}[ERROR]{RESET} Invalid value for {param}: {e}")
                
                elif subcmd == "message":
                    if len(parts) < 3:
                        print(f"{RED}[ERROR]{RESET} Usage: auto message <text>")
                        continue
                    
                    message = " ".join(parts[2:])
                    protocol.queue_auto_message(message)
                    print(f"{GREEN}[AUTO]{RESET} Message queued for sending: {message}")
                
                elif subcmd == "passive":
                    # Configure as passive peer (responds but doesn't initiate)
                    protocol.configure_auto_mode(
                        ping_response_accept=True,
                        ping_auto_initiate=False,
                        active_mode=False
                    )
                    print(f"{GREEN}[AUTO]{RESET} Configured as passive peer")
                
                elif subcmd == "active":
                    # Configure as active peer (initiates protocol)
                    protocol.configure_auto_mode(
                        ping_response_accept=True,
                        ping_auto_initiate=True,
                        active_mode=True
                    )
                    print(f"{GREEN}[AUTO]{RESET} Configured as active peer")
                
                else:
                    print(f"{RED}[ERROR]{RESET} Unknown auto mode command: {subcmd}")
                    print("Available commands: start, stop, status, config, message, passive, active")
            
            # Legacy commands
            elif cmd == "auto_responder":
                if len(parts) != 2:
                    print(f"{RED}[ERROR]{RESET} Usage: auto_responder <on|off>")
                    continue
                val = parts[1].lower()
                if val not in ("on", "off"):
                    print(f"{RED}[ERROR]{RESET} Value must be 'on' or 'off'.")
                    continue
                protocol.enable_auto_responder(val == "on")
                print(f"{YELLOW}[WARNING]{RESET} Using legacy auto responder. Consider using 'auto' commands instead.")
                
            else:
                print(f"{RED}[ERROR]{RESET} Unknown command: {cmd}")
                print("Type 'help' for a list of available commands.")
                
        except Exception as e:
            print(f"{RED}[ERROR]{RESET} Command failed: {e}")

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\nExiting...")
    except Exception as e:
        print(f"{RED}[FATAL ERROR]{RESET} {e}")
        sys.exit(1)
