import re
from collections.abc import Callable
from typing import Any

from cooperbench.agents.swe_agent.tooling.commands import Command


def _guard_multiline_input(action: str, match_fct: Callable[[str], re.Match | None]) -> str:
    """Split action by multiline commands, then append the first line in each multiline command with "<< '{end_name}'".
    Multiline commands (which are specified by an end_name) are commands that span multiple lines and are terminated by a specific end_name.

    Their multi-line argument is sent using a heredoc, which is a way to send a multi-line string to a command in bash.
    """
    parsed_action = []
    rem_action = action
    while rem_action.strip():
        first_match = match_fct(rem_action)
        if first_match:
            pre_action = rem_action[: first_match.start()]
            match_action = rem_action[first_match.start() : first_match.end()]
            rem_action = rem_action[first_match.end() :]
            if pre_action.strip():
                parsed_action.append(pre_action)
            if match_action.strip():
                eof = first_match.group(3).strip()
                if not match_action.split("\n")[0].strip().endswith(f"<< '{eof}'"):
                    guarded_command = match_action[first_match.start() :]
                    first_line = guarded_command.split("\n")[0]
                    guarded_command = guarded_command.replace(first_line, first_line + f" << '{eof}'", 1)
                    parsed_action.append(guarded_command)
                else:
                    parsed_action.append(match_action)
        else:
            parsed_action.append(rem_action)
            rem_action = ""
    return "\n".join(parsed_action)


def _should_quote(value: Any, command: Command) -> bool:
    """Returns True if the value should be quoted, False otherwise."""
    if command.name == "bash":
        return False
    return isinstance(value, str) and command.end_name is None


def get_signature(cmd):
    """Generate a command signature from its arguments.

    Args:
        cmd: Command object to generate signature for

    Returns:
        Formatted signature string
    """
    signature = cmd.name
    if "arguments" in cmd.__dict__ and cmd.arguments is not None:
        if cmd.end_name is None:
            for argument in cmd.arguments:
                param = argument.name
                if argument.required:
                    signature += f" <{param}>"
                else:
                    signature += f" [<{param}>]"
        else:
            for argument in cmd.arguments[:-1]:
                param = argument.name
                if argument.required:
                    signature += f" <{param}>"
                else:
                    signature += f" [<{param}>]"
            signature += f"\n{list(cmd.arguments[-1].keys())[0]}\n{cmd.end_name}"
    return signature


def generate_command_docs(
    commands: list[Command],
    subroutine_types,
    **kwargs,
) -> str:
    """Generate detailed command documentation.

    Format includes docstring, signature and argument details.

    Args:
        commands: List of commands to document
        subroutine_types: List of subroutines to document
        **kwargs: Additional format variables for docstrings

    Returns:
        Formatted documentation string
    """
    docs = ""
    for cmd in commands + subroutine_types:
        docs += f"{cmd.name}:\n"
        if cmd.docstring is not None:
            docs += f"  docstring: {cmd.docstring.format(**kwargs)}\n"
        if cmd.signature is not None:
            docs += f"  signature: {cmd.signature}\n"
        else:
            docs += f"  signature: {get_signature(cmd)}\n"
        if cmd.arguments:
            docs += "  arguments:\n"
            for argument in cmd.arguments:
                param = argument.name
                req_string = "required" if argument.required else "optional"
                docs += f"    - {param} ({argument.type}) [{req_string}]: {argument.description}\n"
        docs += "\n"
    return docs
