"""
Client for making remote function calls
"""

import json
import pickle
import urllib.request
import urllib.error
from typing import Any, Callable


DEFAULT_PORT = 5555
DEFAULT_HOST = 'localhost'

_config = {
    'host': DEFAULT_HOST,
    'port': DEFAULT_PORT
}


def configure(host: str = None, port: int = None):
    """
    Configure global settings for remote calls.
    
    Args:
        host: Default server hostname
        port: Default server port
    """
    if host is not None:
        _config['host'] = host
    if port is not None:
        _config['port'] = port


class RemoteCallError(Exception):
    """Exception raised when a remote call fails"""
    pass


def create_remote_function(func_name: str, host: str = None, port: int = None) -> Callable:
    """
    Create a callable that makes HTTP requests to execute a remote function
    
    Args:
        func_name: Qualified name of the function
        host: Server hostname (defaults to configured host)
        port: Server port (defaults to configured port)
        
    Returns:
        A callable that executes the function remotely
    """
    target_host = host or _config['host']
    target_port = port or _config['port']
    
    def remote_call(*args, **kwargs) -> Any:
        """Execute the remote function call"""
        url = f"http://{target_host}:{target_port}/"
        
        # Prepare request payload
        try:
            # Try to serialize with JSON first
            json.dumps(args)
            json.dumps(kwargs)
            payload = {
                'function': func_name,
                'args': args,
                'kwargs': kwargs
            }
        except (TypeError, ValueError):
            # Fall back to pickle for complex objects
            payload = {
                'function': func_name,
                'args_pickle': pickle.dumps(args).hex(),
                'kwargs_pickle': pickle.dumps(kwargs).hex()
            }
        
        request_body = json.dumps(payload).encode('utf-8')
        
        # Make HTTP request
        try:
            req = urllib.request.Request(
                url,
                data=request_body,
                headers={'Content-Type': 'application/json'},
                method='POST'
            )
            
            with urllib.request.urlopen(req, timeout=30) as response:
                response_body = response.read().decode('utf-8')
                result = json.loads(response_body)
                
                if result['success']:
                    if 'result' in result:
                        return result['result']
                    elif 'result_pickle' in result:
                        return pickle.loads(bytes.fromhex(result['result_pickle']))
                    else:
                        return None
                else:
                    error_msg = result.get('error', 'Unknown error')
                    traceback = result.get('traceback', '')
                    raise RemoteCallError(f"Remote function failed: {error_msg}\n{traceback}")
        
        except urllib.error.HTTPError as e:
            error_body = e.read().decode('utf-8')
            try:
                error_data = json.loads(error_body)
                error_msg = error_data.get('error', str(e))
                traceback = error_data.get('traceback', '')
                raise RemoteCallError(f"HTTP {e.code}: {error_msg}\n{traceback}")
            except json.JSONDecodeError:
                raise RemoteCallError(f"HTTP {e.code}: {error_body}")
        
        except urllib.error.URLError as e:
            raise RemoteCallError(f"Failed to connect to server at {url}: {e.reason}")
        
        except Exception as e:
            raise RemoteCallError(f"Unexpected error during remote call: {type(e).__name__}: {str(e)}")
    
    remote_call.__name__ = func_name.split('.')[-1]
    remote_call.__doc__ = f"Remote function call to {func_name}"
    
    return remote_call
