import random
import time

class InvalidActionError(ValueError):
  """Raised when an action has invalid parameters."""

_X_KEYSYM_MAPPING = {
    "Alt_L": "altleft",
    "Alt_R": "altright",
    "Control_L": "ctrlleft",
    "Control_R": "ctrlright",
    "Super_L": "winleft",
    "Super_R": "winright",
    "Shift_L": "shiftleft",
    "Shift_R": "shiftright",
    "Page_Up": "pageup",
    "Page_Down": "pagedown",
    "Caps_Lock": "capslock",
    "Scroll_Lock": "scrolllock",
    "Num_Lock": "numlock",
}

def _normalize_key(key: str) -> str:
    if key in _X_KEYSYM_MAPPING:
        return _X_KEYSYM_MAPPING[key]
    return key.lower()

def convert_to_pyautogui_action(action: dict) -> str:
    """Converts a Gemini action dict to Python pyautogui commands (V4 GCL/Literal)."""
    raw_action_type = action.get("action_type", "")
    if ":" in raw_action_type:
        action_type = raw_action_type.split(":")[-1]
    else:
        action_type = raw_action_type

    parameters = action.get("parameters", {})

    screen_width = 1920
    screen_height = 1080

    def normalize_coord(coord, max_val):
        """Convert normalized coordinate [0, 1000] to actual pixel coordinate."""
        return int((float(coord) / 1000.0) * max_val)

    move_mode = random.choice([
        "pyautogui.easeInQuad",
        "pyautogui.easeOutQuad",
        "pyautogui.easeInOutQuad",
        "pyautogui.easeInBounce",
        "pyautogui.easeInElastic",
    ])
    duration = random.uniform(0.5, 1)

    if action_type == "click":
        x = normalize_coord(parameters["x"], screen_width)
        y = normalize_coord(parameters["y"], screen_height)
        return f"pyautogui.click(x={x}, y={y})"

    elif action_type == "double_click":
        x = normalize_coord(parameters["x"], screen_width)
        y = normalize_coord(parameters["y"], screen_height)
        return f"pyautogui.doubleClick(x={x}, y={y})"

    elif action_type == "triple_click":
        x = normalize_coord(parameters["x"], screen_width)
        y = normalize_coord(parameters["y"], screen_height)
        return f"pyautogui.click(x={x}, y={y}, clicks=3)"

    elif action_type == "middle_click":
        x = normalize_coord(parameters["x"], screen_width)
        y = normalize_coord(parameters["y"], screen_height)
        return f"pyautogui.click(x={x}, y={y}, button='middle')"

    elif action_type == "right_click":
        x = normalize_coord(parameters["x"], screen_width)
        y = normalize_coord(parameters["y"], screen_height)
        return f"pyautogui.click(x={x}, y={y}, button='right')"

    elif action_type == "mouse_down":
        x = normalize_coord(parameters["x"], screen_width)
        y = normalize_coord(parameters["y"], screen_height)
        return f"pyautogui.mouseDown(x={x}, y={y})"

    elif action_type == "mouse_up":
        x = normalize_coord(parameters["x"], screen_width)
        y = normalize_coord(parameters["y"], screen_height)
        return f"pyautogui.mouseUp(x={x}, y={y})"

    elif action_type == "move":
        x = normalize_coord(parameters["x"], screen_width)
        y = normalize_coord(parameters["y"], screen_height)
        return f"pyautogui.moveTo({x}, {y}, {duration}, {move_mode})"

    elif action_type == "type":
        text = parameters.get("text", "")
        press_enter = parameters.get("press_enter", False)
        commands = [f"pyautogui.typewrite({repr(text)})"]
        if press_enter:
            commands.append("pyautogui.press('enter')")
        return "; ".join(commands) if len(commands) > 1 else commands[0]

    elif action_type == "drag_and_drop":
        start_x = normalize_coord(parameters["start_x"], screen_width)
        start_y = normalize_coord(parameters["start_y"], screen_height)
        end_x = normalize_coord(parameters["end_x"], screen_width)
        end_y = normalize_coord(parameters["end_y"], screen_height)
        return (
            f"pyautogui.moveTo({start_x}, {start_y}); pyautogui.dragTo({end_x},"
            f" {end_y}, {duration}, {move_mode})"
        )

    elif action_type == "scroll":
        direction = parameters.get("direction", "down")
        magnitude = 2
        if "magnitude_in_wheel_clicks" in parameters:
            magnitude = parameters["magnitude_in_wheel_clicks"]
        x = normalize_coord(parameters["x"], screen_width)
        y = normalize_coord(parameters["y"], screen_height)
        move_cmd = f"pyautogui.moveTo({x}, {y})"
        if direction == "down":
            scroll_cmd = f"pyautogui.vscroll(-{magnitude})"
        elif direction == "up":
            scroll_cmd = f"pyautogui.vscroll({magnitude})"
        elif direction == "right":
            scroll_cmd = f"pyautogui.hscroll({magnitude})"
        elif direction == "left":
            scroll_cmd = f"pyautogui.hscroll(-{magnitude})"
        else:
            raise ValueError(f"Invalid scroll direction: {direction}")
        return f"{move_cmd}; {scroll_cmd}"

    elif action_type == "wait":
        seconds = parameters.get("seconds", 1.0)
        return f"time.sleep({seconds})"

    elif action_type == "press_key":
        key = _normalize_key(parameters.get("key", ""))
        return f"pyautogui.press('{key}')"

    elif action_type == "key_down":
        key = _normalize_key(parameters.get("key", ""))
        return f"pyautogui.keyDown('{key}')"

    elif action_type == "key_up":
        key = _normalize_key(parameters.get("key", ""))
        return f"pyautogui.keyUp('{key}')"

    elif action_type == "hotkey":
        keys = [_normalize_key(k) for k in parameters.get("keys", [])]
        keys_para_rep = "', '".join(keys)
        return f"pyautogui.hotkey('{keys_para_rep}')"

    elif action_type == "done":
        return "DONE"
    elif action_type == "infeasible":
        return "FAIL"
    elif action_type == "take_screenshot":
        return "WAIT"
    else:
        raise ValueError(f"Unknown action type: {action_type}")
