import socket
import threading
from datetime import datetime

class TerminalChatServer:
    """
    A terminal-based chat server with room access codes.
    
    Args:
        host (str): Server host address (default: '0.0.0.0')
        port (int): Server port (default: 9999)
        rooms (dict): Pre-configured rooms (optional)
    """
    
    def __init__(self, host='0.0.0.0', port=9999, rooms=None):
        self.host = host
        self.port = port
        self.rooms = rooms or {
            "1234": {"name": "General Chat", "clients": [], "password": None},
            "5678": {"name": "Tech Talk", "clients": [], "password": None},
            "9999": {"name": "Secret Room", "clients": [], "password": "secret"}
        }
        self.server_socket = None
        self.running = True
        
    def start_server(self):
        """
        Start the chat server and begin accepting connections.
        """
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        
        try:
            self.server_socket.bind((self.host, self.port))
            self.server_socket.listen(5)
            print(f"🚀 Terminal Chat Server started on {self.host}:{self.port}")
            print("📝 Available rooms:")
            for code, room in self.rooms.items():
                password_info = f" (Password: {room['password']})" if room['password'] else ""
                print(f"   {code}: {room['name']}{password_info}")
            print(f"\n📍 Connect using: telnet {self.host} {self.port}")
            print("📍 Press Ctrl+C to stop the server\n")
            
            self.accept_connections()
            
        except Exception as e:
            print(f"❌ Error starting server: {e}")
        finally:
            self.stop_server()
    
    def accept_connections(self):
        """Accept incoming client connections."""
        while self.running:
            try:
                client_socket, addr = self.server_socket.accept()
                print(f"🔗 New connection from {addr[0]}:{addr[1]}")
                
                client_thread = threading.Thread(
                    target=self.handle_client,
                    args=(client_socket, addr)
                )
                client_thread.daemon = True
                client_thread.start()
                
            except Exception as e:
                if self.running:
                    print(f"❌ Accept error: {e}")
    
    def handle_client(self, client_socket, addr):
        """Handle individual client connection."""
        username = None
        room_code = None
        
        try:
            # Send welcome message
            welcome_msg = """
╔══════════════════════════════════╗
║     🗨️  TERMINAL CHAT SERVER     ║
╚══════════════════════════════════╝
"""
            client_socket.send(welcome_msg.encode())
            client_socket.send(b"\nAvailable rooms:\n")
            for code, room in self.rooms.items():
                client_socket.send(f"  {code}: {room['name']}\n".encode())
            client_socket.send(b"\n")
            
            # Get room code
            client_socket.send(b"Enter room code: ")
            room_code = client_socket.recv(1024).decode().strip()
            
            if room_code not in self.rooms:
                client_socket.send(b"❌ Invalid room code!\n")
                return
            
            room = self.rooms[room_code]
            
            # Check password if required
            if room['password']:
                client_socket.send(b"Enter room password: ")
                password = client_socket.recv(1024).decode().strip()
                if password != room['password']:
                    client_socket.send(b"❌ Wrong password!\n")
                    return
            
            # Get username
            client_socket.send(b"Enter your username: ")
            username = client_socket.recv(1024).decode().strip()
            
            if not username:
                username = f"User{addr[1]}"
            
            # Add client to room
            client_info = {"socket": client_socket, "username": username, "addr": addr}
            room["clients"].append(client_info)
            
            # Welcome user
            join_msg = f"\n✅ {username} joined {room['name']}\n"
            client_socket.send(join_msg.encode())
            client_socket.send(b"Type your messages below. Use /quit to exit.\n")
            client_socket.send(b"─" * 50 + b"\n")
            
            # Notify others
            self.broadcast(room_code, f"👋 {username} joined the chat!\n", exclude=client_socket)
            
            # Chat loop
            while True:
                message = client_socket.recv(1024).decode().strip()
                
                if not message or message.lower() == '/quit':
                    break
                elif message.lower() == '/users':
                    users = [client['username'] for client in room['clients']]
                    client_socket.send(f"👥 Online users: {', '.join(users)}\n".encode())
                elif message.lower() == '/help':
                    help_msg = """
Available commands:
/help    - Show this help
/users   - Show online users
/quit    - Exit chat
"""
                    client_socket.send(help_msg.encode())
                else:
                    timestamp = datetime.now().strftime("%H:%M:%S")
                    full_message = f"[{timestamp}] {username}: {message}\n"
                    self.broadcast(room_code, full_message)
                    
        except Exception as e:
            print(f"❌ Error with client {addr}: {e}")
        finally:
            self.remove_client(room_code, client_socket, username)
            client_socket.close()
    
    def broadcast(self, room_code, message, exclude=None):
        """Broadcast message to all clients in a room."""
        if room_code in self.rooms:
            room = self.rooms[room_code]
            for client in room["clients"][:]:
                try:
                    if client["socket"] != exclude:
                        client["socket"].send(message.encode())
                except:
                    self.remove_client(room_code, client["socket"], client["username"])
    
    def remove_client(self, room_code, client_socket, username):
        """Remove client from room and notify others."""
        if room_code and room_code in self.rooms:
            room = self.rooms[room_code]
            room["clients"] = [c for c in room["clients"] if c["socket"] != client_socket]
            if username:
                leave_msg = f"👋 {username} left the chat\n"
                self.broadcast(room_code, leave_msg)
                print(f"🔌 {username} disconnected from {room['name']}")
    
    def stop_server(self):
        """Stop the server and clean up resources."""
        self.running = False
        if self.server_socket:
            self.server_socket.close()
        print("\n🛑 Server stopped")
    
    def add_room(self, code, name, password=None):
        """Add a new room to the server."""
        self.rooms[code] = {
            "name": name,
            "clients": [],
            "password": password
        }
        print(f"✅ Added room: {code} - {name}")
    
    def remove_room(self, code):
        """Remove a room from the server."""
        if code in self.rooms:
            # Kick all clients from the room
            room = self.rooms[code]
            for client in room["clients"]:
                try:
                    client["socket"].send(b"Room has been removed by administrator.\n")
                    client["socket"].close()
                except:
                    pass
            del self.rooms[code]
            print(f"✅ Removed room: {code}")
