from typing import Callable, Union

from talon import Module, actions

from ..targets.target_types import (
    CursorlessDestination,
    CursorlessExplicitTarget,
    CursorlessTarget,
    ImplicitDestination,
)
from .bring_move import BringMoveTargets
from .execute_command import cursorless_execute_command_action
from .homophones import cursorless_homophones_action
from .replace import cursorless_replace_action

mod = Module()

mod.list(
    "cursorless_simple_action",
    desc="Cursorless internal: simple actions",
)

mod.list(
    "cursorless_callback_action",
    desc="Cursorless internal: actions implemented via a callback function",
)

mod.list(
    "cursorless_custom_action",
    desc="Cursorless internal: user-defined custom actions",
)

mod.list(
    "cursorless_experimental_action",
    desc="Cursorless internal: experimental actions",
)

ACTION_LIST_NAMES = [
    "simple_action",
    "callback_action",
    "paste_action",
    "bring_move_action",
    "swap_action",
    "wrap_action",
    "insert_snippet_action",
    "reformat_action",
    "call_action",
    "experimental_action",
    "custom_action",
]

callback_actions: dict[str, Callable[[CursorlessExplicitTarget], None]] = {
    "nextHomophone": cursorless_homophones_action,
}

# Don't wait for these actions to finish, usually because they hang on some kind of user interaction
no_wait_actions = [
    "generateSnippet",
    "rename",
]

# These are actions that we don't wait for, but still want to have a post action sleep
no_wait_actions_post_sleep = {
    "rename": 0.3,
}


@mod.capture(
    rule=(
        "{user.cursorless_simple_action} |"
        "{user.cursorless_experimental_action} |"
        "{user.cursorless_callback_action} |"
        "{user.cursorless_call_action} |"
        "{user.cursorless_custom_action}"
    )
)
def cursorless_action_or_ide_command(m) -> dict[str, str]:
    try:
        value = m.cursorless_custom_action
        type = "ide_command"
    except AttributeError:
        value = m[0]
        type = "cursorless_action"
    return {
        "value": value,
        "type": type,
    }


@mod.action_class
class Actions:
    def cursorless_command(action_name: str, target: CursorlessExplicitTarget):  # pyright: ignore [reportGeneralTypeIssues]
        """Perform cursorless command on target"""
        if action_name in callback_actions:
            callback_actions[action_name](target)
        elif action_name in ["replaceWithTarget", "moveToTarget"]:
            actions.user.private_cursorless_bring_move(
                action_name, BringMoveTargets(target, ImplicitDestination())
            )
        elif action_name == "callAsFunction":
            actions.user.private_cursorless_call(target)
        elif action_name in no_wait_actions:
            action = {"name": action_name, "target": target}
            actions.user.private_cursorless_command_no_wait(action)
            if action_name in no_wait_actions_post_sleep:
                actions.sleep(no_wait_actions_post_sleep[action_name])
        else:
            action = {"name": action_name, "target": target}
            actions.user.private_cursorless_command_and_wait(action)

    def cursorless_vscode_command(command_id: str, target: CursorlessTarget):  # pyright: ignore [reportGeneralTypeIssues]
        """
        Perform vscode command on cursorless target

        Deprecated: prefer `cursorless_ide_command`
        """
        return actions.user.cursorless_ide_command(command_id, target)

    def cursorless_ide_command(command_id: str, target: CursorlessTarget):  # pyright: ignore [reportGeneralTypeIssues]
        """Perform ide command on cursorless target"""
        return cursorless_execute_command_action(command_id, target)

    def cursorless_insert(
        destination: CursorlessDestination,  # pyright: ignore [reportGeneralTypeIssues]
        text: Union[str, list[str]],
    ):
        """Perform text insertion on Cursorless destination"""
        if isinstance(text, str):
            text = [text]
        cursorless_replace_action(destination, text)

    def private_cursorless_action_or_ide_command(
        instruction: dict[str, str],  # pyright: ignore [reportGeneralTypeIssues]
        target: CursorlessTarget,
    ):
        """Perform cursorless action or ide command on target (internal use only)"""
        type = instruction["type"]
        value = instruction["value"]
        if type == "cursorless_action":
            actions.user.cursorless_command(value, target)
        elif type == "ide_command":
            actions.user.cursorless_ide_command(value, target)
