import os
from typing import Literal

from gamcp.gam_runner import GAM_EXECUTABLE_PATH, execute_gam

def run_gam_command_func(args: list[str]) -> str:
    """
    Escape hatch for GAM commands not covered by a dedicated tool. 
    Prefer the specific semantic tools when available.
    """
    return execute_gam(args)

def validate_gam_command(args: list[str]) -> str:
    """
    Validates the structure of a proposed GAM command before execution.
    Checks executable availability, known top-level verbs, and bulk command
    structural rules. Does NOT execute the command.
    Returns 'VALID' or a descriptive error/warning message.
    """
    if not args:
        return "ERROR: No arguments provided."

    KNOWN_VERBS = {
        "info", "create", "update", "delete", "print", "show", "copy", "move",
        "report", "csv", "loop", "batch", "tbatch", "whatis", "transfer",
        "add", "remove", "sync", "redirect", "user", "users", "group", "groups",
        "calendar", "drive", "report"
    }
    BULK_VERBS = {"csv", "loop", "batch", "tbatch"}

    if args[0] not in KNOWN_VERBS:
        return f"WARNING: '{args[0]}' is not a recognized GAM verb. Verify against the GAM wiki before executing."

    if not os.path.exists(GAM_EXECUTABLE_PATH) or not os.access(GAM_EXECUTABLE_PATH, os.X_OK):
        return f"ERROR: GAM executable not accessible at '{GAM_EXECUTABLE_PATH}'."

    if args[0] in BULK_VERBS:
        if "gsheet" in args:
            idx = args.index("gsheet")
            if len(args) < idx + 4:
                return "ERROR: 'gsheet' source requires operator_email, fileID, and sheetName after 'gsheet'."
        if args[0] == "batch" and "csv" in args:
            return "ERROR: 'gam batch' does not support nested 'gam csv'. Use 'gam tbatch' instead."
        if args[0] in {"csv", "loop"} and "gam" not in args:
            return "ERROR: 'gam csv' and 'gam loop' require an inner 'gam' command template after the source."
        if "redirect" in args and "multiprocess" not in args:
            return "WARNING: Parallel bulk output without 'multiprocess' flag may produce interleaved results."

    return "VALID: Command structure acceptable. Proceed to run_gam_command."

def build_bulk_gam_command(
    source_type: Literal["local_csv", "gsheet", "gdoc", "stdin"],
    processing_mode: Literal["csv", "loop", "batch", "tbatch"],
    gam_command_template: list[str],
    local_file_path: str | None = None,
    operator_email: str | None = None,
    sheet_file_id: str | None = None,
    sheet_name: str | None = None,
    match_field: str | None = None,
    match_pattern: str | None = None,
    skip_field: str | None = None,
    skip_pattern: str | None = None,
    max_rows: int | None = None,
    redirect_to_local_path: str | None = None,
    redirect_to_sheet_id: str | None = None,
    redirect_to_sheet_name: str | None = None,
    show_commands: bool = False,
) -> str:
    """
    Constructs a GAM bulk processing command from structured inputs without executing it.
    Returns the full command string for review. Pass the args to validate_gam_command,
    then run_gam_command if valid.

    gam_command_template uses ~ColumnName for standalone column values and
    ~~ColumnName~~ when embedded within a larger string argument.
    Example: ["update", "user", "~primaryEmail", "ou", "/Staff/~~Department~~"]

    Use processing_mode 'loop' for rate-sensitive or ordered operations.
    Use 'csv' for parallelized operations. Use 'tbatch' when nesting 'gam csv' inside a batch file.
    """
    args = []
    if redirect_to_local_path:
        args.extend(["redirect", "csv", redirect_to_local_path, "multiprocess"])
        if redirect_to_sheet_id:
            args.extend(["todrive", "tdfileid", redirect_to_sheet_id, "tdsheet", f'"{redirect_to_sheet_name or "GAM Output"}"', "tdupdatesheet"])
    elif redirect_to_sheet_id:
        args.extend(["redirect", "csv", "-", "multiprocess", "todrive", "tdfileid", redirect_to_sheet_id, "tdsheet", f'"{redirect_to_sheet_name or "GAM Output"}"', "tdupdatesheet"])

    args.append(processing_mode)

    if source_type == "local_csv":
        if not local_file_path:
            return "ERROR: local_file_path is required for local_csv source_type."
        args.append(local_file_path)
    elif source_type == "gsheet":
        if not operator_email or not sheet_file_id or not sheet_name:
            return "ERROR: operator_email, sheet_file_id, and sheet_name are required for gsheet source_type."
        args.extend(["gsheet", operator_email, sheet_file_id, f'"{sheet_name}"'])
    elif source_type == "gdoc":
        if not operator_email or not sheet_file_id:
            return "ERROR: operator_email and sheet_file_id (doc ID) are required for gdoc source_type."
        args.extend(["gdoc", operator_email, sheet_file_id])
    elif source_type == "stdin":
        args.append("-")

    if match_field and match_pattern:
        args.append("matchfield")
        args.append(str(match_field))
        args.append(str(match_pattern))
    if skip_field and skip_pattern:
        args.append("skipfield")
        args.append(str(skip_field))
        args.append(str(skip_pattern))
    if max_rows is not None:
        args.extend(["maxrows", str(max_rows)])
    if show_commands:
        args.append("showcmds")

    args.append("gam")
    args.extend(gam_command_template)

    cmd_str = " ".join(args)
    return f"BUILT COMMAND (not yet executed):\\n{cmd_str}"

def register(mcp):
    mcp.tool(name="run_gam_command")(run_gam_command_func)
    mcp.tool()(validate_gam_command)
    mcp.tool()(build_bulk_gam_command)
