"""
MineConnect Client - Main CLI and programmatic client interface

Provides both a command-line interface and a Python API for connecting
to MineConnect tunnels.
"""

import time
import threading
from typing import Optional, Callable
from .java_tunnel import JavaTunnel
from .bedrock_tunnel import BedrockTunnel


class MineConnectClient:
    """
    Main client class for connecting to MineConnect tunnels.
    
    Usage:
        client = MineConnectClient(domain="ideadev.me")
        client.connect_java(local_port=25565)
        client.connect_bedrock(local_port=19132)
        # ... play Minecraft ...
        client.disconnect()
    """
    
    def __init__(self, domain: str = "ideadev.me", 
                 on_log: Optional[Callable[[str], None]] = None,
                 on_status: Optional[Callable[[str, str], None]] = None):
        """
        Initialize client.
        
        Args:
            domain: Your server domain (e.g., "ideadev.me")
            on_log: Callback for log messages: on_log(message)
            on_status: Callback for status updates: on_status(tunnel_name, status)
        """
        self.domain = domain
        self.java_hostname = f"java.{domain}"
        self.bedrock_hostname = f"bedrock.{domain}"
        
        self._on_log = on_log or self._default_log
        self._on_status = on_status or self._default_status
        
        self.java_tunnel: Optional[JavaTunnel] = None
        self.bedrock_tunnel: Optional[BedrockTunnel] = None
        
        self._java_status = "disconnected"
        self._bedrock_status = "disconnected"
        self._lock = threading.Lock()
    
    def _default_log(self, msg: str):
        """Default log handler - prints to console."""
        print(f"[MineConnect] {msg}")
    
    def _default_status(self, tunnel: str, status: str):
        """Default status handler."""
        with self._lock:
            if tunnel == "java":
                self._java_status = status
            elif tunnel == "bedrock":
                self._bedrock_status = status
    
    def _make_log_handler(self, tunnel_name: str):
        """Create a log handler for a specific tunnel."""
        def handler(msg: str):
            self._on_log(f"[{tunnel_name}] {msg}")
        return handler
    
    def _make_status_handler(self, tunnel_name: str):
        """Create a status handler for a specific tunnel."""
        def handler(status: str):
            # Always update internal status
            with self._lock:
                if tunnel_name == "java":
                    self._java_status = status
                elif tunnel_name == "bedrock":
                    self._bedrock_status = status
            # Then call custom callback
            self._on_status(tunnel_name, status)
        return handler
    
    def connect_java(self, local_port: int = 25565):
        """
        Connect Java Edition tunnel.
        
        Args:
            local_port: Local port to listen on (default: 25565)
        """
        if self.java_tunnel and self.java_tunnel.running:
            self._on_log("Java tunnel already connected")
            return
        
        self.java_tunnel = JavaTunnel(
            on_log=self._make_log_handler("Java"),
            on_status=self._make_status_handler("java"),
            on_port=lambda p: self._on_log(f"Java tunnel listening on port {p}")
        )
        self.java_tunnel.start(self.java_hostname, local_port)
        self._on_log(f"Connecting Java tunnel to {self.java_hostname}...")
    
    def connect_bedrock(self, local_port: int = 19132):
        """
        Connect Bedrock Edition tunnel.
        
        Args:
            local_port: Local port to listen on (default: 19132)
        """
        if self.bedrock_tunnel and self.bedrock_tunnel.running:
            self._on_log("Bedrock tunnel already connected")
            return
        
        self.bedrock_tunnel = BedrockTunnel(
            on_log=self._make_log_handler("Bedrock"),
            on_status=self._make_status_handler("bedrock"),
            on_port=lambda p: self._on_log(f"Bedrock tunnel listening on port {p}")
        )
        self.bedrock_tunnel.start(self.bedrock_hostname, local_port)
        self._on_log(f"Connecting Bedrock tunnel to {self.bedrock_hostname}...")
    
    def connect_both(self, java_port: int = 25565, bedrock_port: int = 19132):
        """
        Connect both Java and Bedrock tunnels.
        
        Args:
            java_port: Local port for Java Edition (default: 25565)
            bedrock_port: Local port for Bedrock Edition (default: 19132)
        """
        self.connect_java(java_port)
        self.connect_bedrock(bedrock_port)
    
    def disconnect_java(self):
        """Disconnect Java Edition tunnel."""
        if self.java_tunnel:
            self._on_log("Disconnecting Java tunnel...")
            self.java_tunnel.stop()
            self.java_tunnel = None
            self._on_log("Java tunnel disconnected")
    
    def disconnect_bedrock(self):
        """Disconnect Bedrock Edition tunnel."""
        if self.bedrock_tunnel:
            self._on_log("Disconnecting Bedrock tunnel...")
            self.bedrock_tunnel.stop()
            self.bedrock_tunnel = None
            self._on_log("Bedrock tunnel disconnected")
    
    def disconnect(self):
        """Disconnect all tunnels."""
        self.disconnect_java()
        self.disconnect_bedrock()
    
    def get_status(self) -> dict:
        """
        Get current connection status.
        
        Returns:
            dict with 'java' and 'bedrock' status strings
        """
        with self._lock:
            return {
                "java": self._java_status,
                "bedrock": self._bedrock_status,
                "connected": self._java_status == "connected" or self._bedrock_status == "connected",
            }
    
    def is_connected(self) -> bool:
        """Check if any tunnel is connected."""
        return self.get_status()["connected"]
    
    def wait_for_connection(self, timeout: float = 30.0) -> bool:
        """
        Wait until at least one tunnel connects.
        
        Args:
            timeout: Maximum seconds to wait
            
        Returns:
            True if connected, False if timeout
        """
        start = time.time()
        while time.time() - start < timeout:
            if self.is_connected():
                return True
            time.sleep(0.5)
        return False
