#!/usr/bin/env python3
"""
BB10 Chat CLI Application
Handles encrypted messaging with PIN-based authentication
"""

import json
import os
import sys
import requests
import hashlib
import base64
import time

class BB10ChatCLI:
    def __init__(self):
        self.chat_file = "chat.json"
        self.pin = None
        self.encryption_key = None
        self.php_endpoint = "https://berrystore.sw7ft.com/apps/chat.php"
        
        # Initialize chat file if it doesn't exist
        if not os.path.exists(self.chat_file):
            self.initialize_chat_file()
    
    def initialize_chat_file(self):
        """Initialize the chat.json file with empty structure"""
        initial_data = {
            "messages": [],
            "users": {},
            "public_messages": []
        }
        with open(self.chat_file, 'w') as f:
            json.dump(initial_data, f, indent=2)
    
    def get_pin(self):
        """Get user's BB10 PIN for encryption"""
        if self.pin:
            return self.pin
            
        print("🔐 BB10 Chat - PIN Required")
        print("Enter your BB10 device PIN (e.g., 2B11B48C):")
        pin = input("PIN: ").strip().upper()
        
        if not pin or len(pin) != 8:
            print("❌ Invalid PIN format. PIN must be 8 characters (e.g., 2B11B48C)")
            return None
            
        self.pin = pin
        # Generate encryption key from PIN
        pin_hash = hashlib.sha256(pin.encode()).digest()
        self.encryption_key = base64.b64encode(pin_hash[:32]).decode()
        return pin
    
    def encrypt_message(self, message):
        """Encrypt a message using simple XOR with PIN-based key"""
        if not self.encryption_key:
            return None
        
        # Convert message to bytes
        message_bytes = message.encode('utf-8')
        key_bytes = self.encryption_key.encode('utf-8')
        
        # Simple XOR encryption
        encrypted_bytes = bytearray()
        for i, byte in enumerate(message_bytes):
            encrypted_bytes.append(byte ^ key_bytes[i % len(key_bytes)])
        
        # Return base64 encoded string
        return base64.b64encode(encrypted_bytes).decode('utf-8')
    
    def decrypt_message(self, encrypted_message):
        """Decrypt a message using simple XOR with PIN-based key"""
        if not self.encryption_key:
            return None
        try:
            # Decode base64
            encrypted_bytes = base64.b64decode(encrypted_message.encode('utf-8'))
            key_bytes = self.encryption_key.encode('utf-8')
            
            # Simple XOR decryption
            decrypted_bytes = bytearray()
            for i, byte in enumerate(encrypted_bytes):
                decrypted_bytes.append(byte ^ key_bytes[i % len(key_bytes)])
            
            return decrypted_bytes.decode('utf-8')
        except:
            return "[ENCRYPTED - PIN REQUIRED]"
    
    def add_message(self, sender_pin, message, message_type="public", recipients=None):
        """Add a new message via PHP endpoint"""
        try:
            # Prepare data for PHP endpoint
            data = {
                "action": "send_message",
                "pin": sender_pin,
                "message": message,
                "type": message_type,
                "recipients": recipients or []
            }
            
            # Send POST request to PHP endpoint
            response = requests.post(self.php_endpoint, json=data, timeout=10)
            
            if response.status_code == 200:
                result = response.json()
                if result.get('success'):
                    return True
                else:
                    print(f"❌ PHP Error: {result.get('error', 'Unknown error')}")
                    return False
            else:
                print(f"❌ HTTP Error: {response.status_code}")
                return False
                
        except requests.exceptions.RequestException as e:
            print(f"❌ Network Error: {e}")
            # Fallback to local storage if PHP endpoint is unavailable
            return self.add_message_local(sender_pin, message, message_type, recipients)
        except Exception as e:
            print(f"❌ Error adding message: {e}")
            return False
    
    def add_message_local(self, sender_pin, message, message_type="public", recipients=None):
        """Add a new message to local chat.json (fallback)"""
        try:
            with open(self.chat_file, 'r') as f:
                data = json.load(f)
            
            encrypted_message = self.encrypt_message(message)
            timestamp = int(time.time())
            
            new_message = {
                "id": len(data["messages"]) + 1,
                "sender": sender_pin,
                "message": encrypted_message,
                "type": message_type,
                "timestamp": timestamp,
                "recipients": recipients or []
            }
            
            data["messages"].append(new_message)
            
            # Add to public messages if it's public
            if message_type == "public":
                data["public_messages"].append(new_message)
            
            # Store user info
            if sender_pin not in data["users"]:
                data["users"][sender_pin] = {
                    "first_seen": timestamp,
                    "last_seen": timestamp,
                    "message_count": 1
                }
            else:
                data["users"][sender_pin]["last_seen"] = timestamp
                data["users"][sender_pin]["message_count"] += 1
            
            with open(self.chat_file, 'w') as f:
                json.dump(data, f, indent=2)
            
            return True
        except Exception as e:
            print(f"❌ Error adding message locally: {e}")
            return False
    
    def get_messages(self, limit=10, message_type="public", sender_pin=None):
        """Get recent messages via PHP endpoint"""
        try:
            # Prepare parameters for PHP endpoint
            params = {
                "action": "messages",
                "type": message_type,
                "limit": limit
            }
            
            if sender_pin:
                params["pin"] = sender_pin
            
            # Send GET request to PHP endpoint
            response = requests.get(self.php_endpoint, params=params, timeout=10)
            
            if response.status_code == 200:
                result = response.json()
                if result.get('success'):
                    return result['data']['messages']
                else:
                    print(f"❌ PHP Error: {result.get('error', 'Unknown error')}")
                    return []
            else:
                print(f"❌ HTTP Error: {response.status_code}")
                return []
                
        except requests.exceptions.RequestException as e:
            print(f"❌ Network Error: {e}")
            # Fallback to local storage if PHP endpoint is unavailable
            return self.get_messages_local(limit, message_type, sender_pin)
        except Exception as e:
            print(f"❌ Error getting messages: {e}")
            return []
    
    def get_messages_local(self, limit=10, message_type="public", sender_pin=None):
        """Get recent messages from local chat.json (fallback)"""
        try:
            with open(self.chat_file, 'r') as f:
                data = json.load(f)
            
            messages = []
            
            if message_type == "public":
                messages = data["public_messages"][-limit:]
            elif message_type == "dm":
                # Get DMs where user is sender or recipient
                for msg in data["messages"]:
                    if (msg["type"] == "dm" and 
                        (msg["sender"] == sender_pin or sender_pin in msg["recipients"])):
                        messages.append(msg)
                messages = messages[-limit:]
            
            return messages
        except Exception as e:
            print(f"❌ Error getting messages locally: {e}")
            return []
    
    def display_messages(self, messages):
        """Display messages in a formatted way"""
        if not messages:
            print("📭 No messages found")
            return
        
        print(f"\n📨 Last {len(messages)} messages:")
        print("-" * 50)
        
        for msg in messages:
            timestamp = time.strftime("%H:%M:%S", time.localtime(msg["timestamp"]))
            sender = msg["sender"]
            
            # Check if message is already decrypted (from PHP endpoint)
            if "decrypted_message" in msg:
                decrypted_message = msg["decrypted_message"]
            else:
                decrypted_message = self.decrypt_message(msg["message"])
            
            if msg["type"] == "dm":
                recipients = ", ".join(msg["recipients"])
                print(f"[{timestamp}] {sender} → {recipients}: {decrypted_message}")
            else:
                print(f"[{timestamp}] {sender}: {decrypted_message}")
        
        print("-" * 50)
    
    def handle_msg_command(self, args):
        """Handle the 'msg' command"""
        if len(args) < 1:
            print("❌ Usage: msg <message> or msg <pin> <message> or msg <pin1> <pin2> <message>")
            return
        
        pin = self.get_pin()
        if not pin:
            return
        
        if len(args) == 1:
            # Public message: msg hello
            message = args[0]
            if self.add_message(pin, message, "public"):
                print(f"✅ Public message sent: {message}")
        
        elif len(args) == 2:
            # DM to specific user: msg <pin> hello
            recipient_pin = args[0].upper()
            message = args[1]
            
            if len(recipient_pin) != 8:
                print("❌ Invalid recipient PIN format")
                return
            
            if self.add_message(pin, message, "dm", [recipient_pin]):
                print(f"✅ DM sent to {recipient_pin}: {message}")
        
        elif len(args) >= 3:
            # Group DM: msg <pin1> <pin2> hello
            recipient_pins = [args[0].upper(), args[1].upper()]
            message = " ".join(args[2:])
            
            for rpin in recipient_pins:
                if len(rpin) != 8:
                    print(f"❌ Invalid PIN format: {rpin}")
                    return
            
            if self.add_message(pin, message, "dm", recipient_pins):
                print(f"✅ Group DM sent to {', '.join(recipient_pins)}: {message}")
    
    def handle_msgs_command(self, args):
        """Handle the 'msgs' command"""
        pin = self.get_pin()
        if not pin:
            return
        
        limit = 10
        if args and args[0].isdigit():
            limit = int(args[0])
        
        messages = self.get_messages(limit, "public", pin)
        self.display_messages(messages)
    
    def handle_dms_command(self, args):
        """Handle the 'dms' command for direct messages"""
        pin = self.get_pin()
        if not pin:
            return
        
        limit = 10
        if args and args[0].isdigit():
            limit = int(args[0])
        
        messages = self.get_messages(limit, "dm", pin)
        self.display_messages(messages)
    
    def handle_users_command(self):
        """Handle the 'users' command to show registered users"""
        try:
            # Send GET request to PHP endpoint
            params = {"action": "users"}
            response = requests.get(self.php_endpoint, params=params, timeout=10)
            
            if response.status_code == 200:
                result = response.json()
                if result.get('success'):
                    users = result['data']['users']
                    print("\n👥 Registered Users:")
                    print("-" * 30)
                    for user in users:
                        last_seen = time.strftime("%H:%M:%S", time.localtime(user["last_seen"]))
                        print(f"{user['pin']}: {user['message_count']} messages (last: {last_seen})")
                    print("-" * 30)
                else:
                    print(f"❌ PHP Error: {result.get('error', 'Unknown error')}")
            else:
                print(f"❌ HTTP Error: {response.status_code}")
                
        except requests.exceptions.RequestException as e:
            print(f"❌ Network Error: {e}")
            # Fallback to local storage
            self.handle_users_command_local()
        except Exception as e:
            print(f"❌ Error getting users: {e}")
    
    def handle_users_command_local(self):
        """Handle the 'users' command from local storage (fallback)"""
        try:
            with open(self.chat_file, 'r') as f:
                data = json.load(f)
            
            print("\n👥 Registered Users (Local):")
            print("-" * 30)
            for user_pin, user_info in data["users"].items():
                last_seen = time.strftime("%H:%M:%S", time.localtime(user_info["last_seen"]))
                print(f"{user_pin}: {user_info['message_count']} messages (last: {last_seen})")
            print("-" * 30)
        except Exception as e:
            print(f"❌ Error getting users locally: {e}")
    
    def show_help(self):
        """Show help information"""
        print("\n💬 BB10 Chat CLI - Help")
        print("=" * 40)
        print("Commands:")
        print("  msg <message>              - Send public message")
        print("  msg <pin> <message>        - Send DM to user")
        print("  msg <pin1> <pin2> <msg>    - Send group DM")
        print("  msgs [count]               - Show last public messages (default: 10)")
        print("  dms [count]                - Show your DMs (default: 10)")
        print("  users                      - Show registered users")
        print("  help                       - Show this help")
        print("  quit                       - Exit chat")
        print("=" * 40)
        print("Example:")
        print("  msg Hello world!           - Public message")
        print("  msg 2B11B48C Hi there!     - DM to user 2B11B48C")
        print("  msg 2B11B48C 1A2B3C4D Hey! - Group DM")
        print("  msgs 5                     - Last 5 public messages")
        print("=" * 40)
    
    def run(self):
        """Main CLI loop"""
        print("💬 BB10 Chat CLI")
        print("Type 'help' for commands, 'quit' to exit")
        
        while True:
            try:
                command = input("\nchat> ").strip()
                
                if not command:
                    continue
                
                if command.lower() in ['quit', 'exit', 'q']:
                    print("👋 Goodbye!")
                    break
                
                if command.lower() == 'help':
                    self.show_help()
                    continue
                
                parts = command.split()
                cmd = parts[0].lower()
                args = parts[1:] if len(parts) > 1 else []
                
                if cmd == 'msg':
                    self.handle_msg_command(args)
                elif cmd == 'msgs':
                    self.handle_msgs_command(args)
                elif cmd == 'dms':
                    self.handle_dms_command(args)
                elif cmd == 'users':
                    self.handle_users_command()
                else:
                    print(f"❌ Unknown command: {cmd}")
                    print("Type 'help' for available commands")
            
            except KeyboardInterrupt:
                print("\n👋 Goodbye!")
                break
            except Exception as e:
                print(f"❌ Error: {e}")

if __name__ == "__main__":
    # All dependencies are built-in Python libraries
    chat = BB10ChatCLI()
    chat.run() 