# pluto_remote_server.py

# from ....client import *
from ...common.utils.process_arg import unmap_arg, map_arg
from ...server.device_manager import get_device  

class PlutoServer:  # host-side server
    @staticmethod
    def _resolve_gid(args) -> int | None:
        """
        Prefer forwarded gid keys:
          - 'g'  (your server fwd_args['g'] = map_arg(int(gid)))
          - 'gid' (optional compat)
        Returns int gid, or None.
        """
        for key in ("g", "gid"):
            if key in args:
                try:
                    return int(unmap_arg(args[key]))
                except Exception:
                    return None
        return None

    @staticmethod
    def _get_device_from_args(args):
        gid = PlutoServer._resolve_gid(args)
        if gid is None:
            return None, "Missing device id (expected args['g'] or args['gid'])."
        dev = get_device(id=int(gid))
        if dev is None:
            return None, f"Unknown/Disconnected device gid={gid} on this host."
        return dev, None

    @staticmethod
    def handle_call(*, function_name, args):
        try:
            function_name_args = function_name.split(':')
            function_name = function_name_args[1]
            function_type = function_name_args[2]
            
            print(f"0 : Pluto RPC: {function_name}, ARGS: {args}")

            # Resolve device ONCE (gid-based)
            device, err = PlutoServer._get_device_from_args(args)
            if device is None:
                return {function_name: map_arg('None'), "UE": map_arg(err)}

            if function_name == "ip":
                # host side: ignore / no-op
                return {}
            
            print(f"1 : Pluto RPC: {function_name}, ARGS: {args}")

            if function_type == "GET":
                return PlutoServer.get(function_name=function_name, device=device)
            
            print(f"2 : Pluto RPC: {function_name}, ARGS: {args}")

            if function_type == "SET":
                return PlutoServer.set(function_name=function_name, value=args[function_name], device=device)
            
            print(f"3 : Pluto RPC: {function_name}, ARGS: {args}")

            if function_type in ("CALL0", "CALL1"):
                return PlutoServer.call_func(
                    function_name=function_name,
                    function_type=function_type,
                    args=args,
                    device=device,
                )

            raise ValueError(f"Unknown function_type: {function_type}")

        except Exception as e:
            return {function_name: map_arg('None'), "Error": map_arg(f'{e}')}

    @staticmethod
    def call_func(*, function_name, function_type, args, device):
        method = getattr(device, function_name, None)
        if not callable(method):
            raise ValueError(f"Device attribute '{function_name}' is not callable.")

        if function_type == "CALL0":
            result = method()
        elif function_type == "CALL1":
            if 'arg1' not in args:
                raise ValueError("CALL1 requires 'arg1' in args.")
            python_arg = unmap_arg(args['arg1'])
            result = method(python_arg)
        else:
            raise ValueError(f"Unsupported call type: {function_type}")
        
        mapped = map_arg(result)
        print("RESULT TYPE:", type(result), "MAPPED FIELDS:", mapped.ListFields())

        return {function_name: map_arg(result if result is not None else 'None')}

    @staticmethod
    def get(*, function_name, device):
        if not hasattr(device, function_name):
            return {function_name: map_arg('None'), "UE": map_arg(f'{function_name} is not a gettable value.')}

        attr = getattr(device, function_name)
        return {function_name: map_arg(attr() if callable(attr) else attr)}

    @staticmethod
    def set(*, function_name, value, device):
        if not hasattr(device, function_name):
            return {function_name: map_arg('None'), "UE": map_arg(f'{function_name} is not a settable value.')}

        setattr(device, function_name, unmap_arg(value))
        return {}