"""Arcade Starter Tools for Calendly

DO NOT EDIT THIS MODULE DIRECTLY.

THIS MODULE WAS AUTO-GENERATED BY TRANSPILING THE API STARTER TOOL JSON DEFINITIONS
IN THE ../wrapper_tools DIRECTORY INTO PYTHON CODE. ANY CHANGES TO THIS MODULE WILL
BE OVERWRITTEN BY THE TRANSPILER.
"""

import asyncio
import json
from enum import Enum
from typing import Annotated, Any

import httpx
import jsonschema
from arcade_tdk import ToolContext, tool
from arcade_tdk.auth import OAuth2
from arcade_tdk.errors import RetryableToolError

from .request_body_schemas import REQUEST_BODY_SCHEMAS

# Retry configuration
INITIAL_RETRY_DELAY = 0.5  # seconds

HTTP_CLIENT = httpx.AsyncClient(
    timeout=httpx.Timeout(60.0, connect=10.0),
    limits=httpx.Limits(max_keepalive_connections=20, max_connections=100),
    transport=httpx.AsyncHTTPTransport(retries=3),
    http2=True,
    follow_redirects=True,
)


class ToolMode(str, Enum):
    """Mode for tools with complex request bodies."""

    GET_REQUEST_SCHEMA = "get_request_schema"
    EXECUTE = "execute"


def remove_none_values(data: dict[str, Any]) -> dict[str, Any]:
    return {k: v for k, v in data.items() if v is not None}


async def make_request(
    url: str,
    method: str,
    params: dict[str, Any] | None = None,
    headers: dict[str, Any] | None = None,
    content: str | None = None,
    data: dict[str, Any] | None = None,
    auth: tuple[str, str] | None = None,
    max_retries: int = 3,
) -> httpx.Response:
    """Make an HTTP request with retry logic for 5xx server errors."""
    for attempt in range(max_retries):
        try:
            response = await HTTP_CLIENT.request(
                url=url,
                auth=auth,
                method=method,
                params=params,
                headers=headers,
                content=content,
            )
            response.raise_for_status()
        except httpx.HTTPStatusError as e:
            # Only retry on 5xx server errors
            if e.response.status_code >= 500 and attempt < max_retries - 1:
                # Exponential backoff: 0.5s, 1s, 2s
                await asyncio.sleep(INITIAL_RETRY_DELAY * (2**attempt))
                continue
            # Re-raise for 4xx errors or if max retries reached
            raise
        except httpx.RequestError:
            # Don't retry request errors (network issues are handled by transport)
            raise
        else:
            return response

    # This should never be reached, but satisfies type checker
    raise httpx.RequestError("Max retries exceeded")  # noqa: TRY003


async def make_request_with_schema_validation(
    url: str,
    method: str,
    request_data: dict[str, Any],
    schema: dict[str, Any] | str,
    auth: tuple[str, str] | None = None,
    params: dict[str, Any] | None = None,
    headers: dict[str, Any] | None = None,
    max_retries: int = 3,
) -> httpx.Response:
    """Make an HTTP request with schema validation on format errors."""
    # Parse schema if it's a string, skip validation if parsing fails
    parsed_schema = None
    if isinstance(schema, str):
        try:
            parsed_schema = json.loads(schema)
        except Exception:
            # If schema parsing fails, just skip validation
            parsed_schema = None
    else:
        parsed_schema = schema

    try:
        response = await make_request(
            url=url,
            auth=auth,
            method=method,
            params=params,
            headers=headers,
            content=json.dumps(request_data),
            max_retries=max_retries,
        )
    except httpx.HTTPStatusError as e:
        # Only provide schema validation for format-related errors
        if e.response.status_code in (400, 422):
            api_error_details = f"API returned {e.response.status_code}: {e.response.text}"

            # Only run validation if we have a valid parsed schema
            if parsed_schema is not None:
                # Run validation to provide additional context
                is_valid, validation_error = validate_json_against_schema(
                    request_data, parsed_schema
                )

                if not is_valid:
                    # Schema validation found issues - additional context
                    additional_context = (
                        f"{api_error_details}\n\n"
                        f"Schema validation found the following issues:\n"
                        f"{validation_error}"
                    )
                else:
                    # Schema validation passed - just show API error
                    additional_context = api_error_details
            else:
                # No valid schema - just show API error
                additional_context = api_error_details

            raise RetryableToolError(
                message=(f"API request failed with validation error: {e.response.status_code}"),
                developer_message=api_error_details,
                additional_prompt_content=additional_context,
            ) from e
        else:
            # For non-validation errors, re-raise as-is
            raise
    else:
        return response


def validate_json_against_schema(
    json_data: dict[str, Any], schema: dict[str, Any]
) -> tuple[bool, str | None]:
    """Validate JSON data against an OpenAPI/JSON Schema.

    This provides full JSON Schema Draft 7 validation including:
    - Required fields, types, enums
    - Pattern validation (regex)
    - Format validation (email, uuid, date-time, etc.)
    - Min/max length and values
    - oneOf, anyOf, allOf
    - And all other JSON Schema features

    Args:
        json_data: The JSON data to validate
        schema: The JSON Schema to validate against

    Returns:
        Tuple of (is_valid, error_messages). If valid, error_messages is None.
        If invalid, error_messages contains all validation errors.
    """
    try:
        validator = jsonschema.Draft7Validator(
            schema, format_checker=jsonschema.Draft7Validator.FORMAT_CHECKER
        )
        # Collect ALL validation errors
        errors = list(validator.iter_errors(json_data))
        if errors:
            # Format all errors with their paths
            error_messages = []
            for error in errors:
                error_path = ".".join(str(p) for p in error.path) if error.path else "root"
                error_messages.append(f"{error.message} at {error_path}")
            # Join all errors with newlines
            return False, "\n".join(error_messages)
        else:
            return True, None
    except jsonschema.SchemaError as e:
        return False, f"Invalid schema: {e.message}"
    except Exception as e:
        return False, f"Validation error: {e!s}"


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_event_invitees(
    context: ToolContext,
    event_uuid: Annotated[
        str, "The unique identifier for the event whose invitees are being retrieved."
    ],
    filter_by_email: Annotated[
        str | None, "Specify an email address to filter invitees by email."
    ] = None,
    invitee_status: Annotated[
        str | None,
        "Filter invitees by their status: 'active' for current invitees or 'canceled' for those who have canceled.",  # noqa: E501
    ] = None,
    number_of_invitees_to_return: Annotated[
        float | None, "Specifies the number of invitee rows to return in the response."
    ] = 20,
    pagination_token: Annotated[
        str | None,
        "A token to retrieve the next or previous page of invitees. Useful for paginated responses.",  # noqa: E501
    ] = None,
    sort_order_by_created_at: Annotated[
        str | None,
        "Specify the order of results based on the creation date as 'asc' for ascending or 'desc' for descending.",  # noqa: E501
    ] = "created_at:asc",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-event-invitees'."]:
    """Retrieve a list of invitees for a given event.

    Use this tool to obtain details about all invitees for a specific event. The tool is suitable when you need to see who has been invited to an event using its unique identifier."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/scheduled_events/{uuid}/invitees".format(uuid=event_uuid),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "status": invitee_status,
            "sort": sort_order_by_created_at,
            "email": filter_by_email,
            "page_token": pagination_token,
            "count": number_of_invitees_to_return,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_scheduled_events(
    context: ToolContext,
    event_status: Annotated[str | None, "Indicate if the event is 'active' or 'canceled'."] = None,
    events_sort_order: Annotated[
        str | None, "Specify sorting order for events. Use 'start_time:asc' or 'start_time:desc'."
    ] = None,
    group_uri: Annotated[
        str | None,
        "URI of the group to fetch scheduled events for. Requires admin/owner or group admin privilege.",  # noqa: E501
    ] = None,
    invitee_email: Annotated[
        str | None, "Email address of the invitee to filter and return related scheduled events."
    ] = None,
    max_start_time_utc: Annotated[
        str | None,
        "Include events with start times prior to this UTC time. Format: YYYY-MM-DDTHH:MM:SS.ssssssZ",  # noqa: E501
    ] = None,
    min_start_time: Annotated[
        str | None,
        'Include events starting after this UTC time. Format: "2020-01-02T03:04:05.678123Z".',
    ] = None,
    number_of_rows_to_return: Annotated[
        float | None, "Specify the number of event entries to retrieve."
    ] = 20,
    organization_uri: Annotated[
        str | None,
        "URI of the organization to retrieve scheduled events for. Requires admin/owner privileges.",  # noqa: E501
    ] = None,
    pagination_token: Annotated[
        str | None, "Token for navigating to the next or previous set of scheduled events."
    ] = None,
    user_uri: Annotated[
        str | None,
        "URI identifying the user for whom to return scheduled events. Use alone for personal events or with 'organization' for specific user events within an organization.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-scheduled-events'."]:
    """Retrieve a list of scheduled events from Calendly.

    Use this tool to get scheduled events based on specific criteria. Pass the 'organization' parameter to fetch events for an organization, 'user' for personal or specific user events, and 'group' for group events. Admins or owners have additional privileges for broader queries."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/scheduled_events",
        method="GET",
        params=remove_none_values({
            "user": user_uri,
            "organization": organization_uri,
            "invitee_email": invitee_email,
            "status": event_status,
            "sort": events_sort_order,
            "min_start_time": min_start_time,
            "max_start_time": max_start_time_utc,
            "page_token": pagination_token,
            "count": number_of_rows_to_return,
            "group": group_uri,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_event_types(
    context: ToolContext,
    number_of_event_types_to_return: Annotated[
        float | None,
        "The number of event types to return. Specify the desired count of returned rows.",
    ] = 20,
    only_admin_managed: Annotated[
        bool | None,
        "Return only admin managed event types if true, exclude them if false, or include all if omitted.",  # noqa: E501
    ] = None,
    organization_uri: Annotated[
        str | None, "URI to view available personal, team, and organization event types."
    ] = None,
    pagination_token: Annotated[
        str | None, "Token to retrieve the next or previous set of event types in pagination."
    ] = None,
    return_active_event_types_only: Annotated[
        bool | None,
        "Set to true to return only active event types, false for only inactive, or omit to include all event types.",  # noqa: E501
    ] = None,
    sort_event_types_by: Annotated[
        str | None,
        "Specify field and direction to order results. Use {field}:{asc/desc}. Fields: name, position, created_at, updated_at.",  # noqa: E501
    ] = "name:asc",
    user_availability_schedule_filter: Annotated[
        str | None,
        "Filters event types by the given primary availability schedule when used with the 'user' parameter.",  # noqa: E501
    ] = None,
    user_uri: Annotated[
        str | None,
        "The user's URI to view associated personal, team, and organization event types.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-event-types'."]:
    """Fetches event types for a specified user or organization.

    This tool retrieves all event types associated with a specified user or organization in Calendly. Use it when you need to get a list of event types for scheduling or management purposes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/event_types",
        method="GET",
        params=remove_none_values({
            "active": return_active_event_types_only,
            "organization": organization_uri,
            "user": user_uri,
            "user_availability_schedule": user_availability_schedule_filter,
            "sort": sort_event_types_by,
            "admin_managed": only_admin_managed,
            "page_token": pagination_token,
            "count": number_of_event_types_to_return,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def create_event_type(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'create-event-type'."]:
    """Create a new one-on-one event type in Calendly.

    This tool is used to create a new one-on-one event type in Calendly. It is useful when scheduling personalized meetings or appointments.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEEVENTTYPE"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["CREATEEVENTTYPE"] + "\n```"
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["CREATEEVENTTYPE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.calendly.com/event_types",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEEVENTTYPE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_calendly_user_info(
    context: ToolContext,
    user_unique_identifier: Annotated[
        str, "The unique identifier of the user. Use 'me' to reference the caller."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-user'."]:
    """Retrieve user information from Calendly.

    Call this tool to get detailed information about a specific Calendly user using their UUID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/users/{uuid}".format(uuid=user_unique_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_user_account_info(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-current-user'."]:
    """Retrieve basic information about the current Calendly user."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/users/me",
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_event_invitee_info(
    context: ToolContext,
    event_unique_identifier: Annotated[
        str,
        "The unique identifier for the specific Calendly event. Use this to specify which event's invitee details to retrieve.",  # noqa: E501
    ],
    invitee_unique_identifier: Annotated[
        str, "The unique identifier of the invitee for a specific event."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-scheduled-event-invitees'."]:
    """Fetch information about a specific event invitee.

    Use this tool to obtain detailed information about an invitee for a particular scheduled event on Calendly. Call this when you need specifics about a person invited to an event, identified by event and invitee UUIDs."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/scheduled_events/{event_uuid}/invitees/{invitee_uuid}".format(  # noqa: UP032
            event_uuid=event_unique_identifier, invitee_uuid=invitee_unique_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def create_event_invitee(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'post-invitee'."]:
    """Create a new event invitee on Calendly.

    This tool is used to create a new event invitee on Calendly. It triggers standard notifications, calendar invites, reschedules, and workflows as if booked via the Calendly interface. Note that only users on paid Calendly plans can access this feature.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEEVENTINVITEE"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["CREATEEVENTINVITEE"] + "\n```"
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["CREATEEVENTINVITEE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.calendly.com/invitees",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEEVENTINVITEE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_event_details(
    context: ToolContext,
    event_unique_identifier: Annotated[
        str, "The unique identifier for the event to retrieve details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-scheduled-event'."]:
    """Retrieve information about a specified scheduled event.

    Use this tool to get details about a specific event scheduled in Calendly, using the event's unique identifier."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/scheduled_events/{uuid}".format(uuid=event_unique_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_event_type_info(
    context: ToolContext,
    event_type_uuid: Annotated[
        str, "The unique identifier (UUID) for the event type to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-event-type'."]:
    """Retrieve information about a specified event type on Calendly.

    Use this tool to obtain detailed information about a specific event type by providing its unique identifier (UUID). It's useful for understanding the configuration and settings of different event types in your Calendly account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/event_types/{uuid}".format(uuid=event_type_uuid),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def update_event_type(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    event_type_uuid: Annotated[
        str | None,
        "The unique identifier for the event type to be updated. This is essential for specifying which one-on-one event type you intend to modify.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'update-event-type'."]:
    """Update details of an existing event type with Calendly.

    Use this tool to update an existing one-on-one event type in Calendly. This tool is specifically for modifying event types categorized as 'solo' or one-on-one.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEEVENTTYPE"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not event_type_uuid:
        missing_params.append(("event_type_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["UPDATEEVENTTYPE"] + "\n```"
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["UPDATEEVENTTYPE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.calendly.com/event_types/{uuid}".format(uuid=event_type_uuid),  # noqa: UP032
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEEVENTTYPE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_organization_invitations(
    context: ToolContext,
    organization_unique_identifier: Annotated[
        str, "The unique identifier for the organization to retrieve invitations for."
    ],
    filter_by_email: Annotated[
        str | None, "Email address to filter the results of organization invitations."
    ] = None,
    filter_by_status: Annotated[
        str | None, "Filter results by invitation status: 'pending', 'accepted', or 'declined'."
    ] = None,
    pagination_token: Annotated[
        str | None, "Token for fetching the next or previous set of organization invitations."
    ] = None,
    rows_to_return: Annotated[
        float | None,
        "Specify the number of organization invitation records to retrieve in the request.",
    ] = 20,
    sort_order: Annotated[
        str | None,
        "Specify the field and direction (ascending or descending) for sorting results. Use a comma-separated list for multiple criteria.",  # noqa: E501
    ] = "created_at:asc",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-organization-invitations'."]:
    """Retrieve organization invitations sent to members.

    Use this tool to get a list of invitations that have been sent to members of a specific organization. This tool is useful for tracking pending or accepted invitations within the organization."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/organizations/{uuid}/invitations".format(  # noqa: UP032
            uuid=organization_unique_identifier
        ),
        method="GET",
        params=remove_none_values({
            "count": rows_to_return,
            "page_token": pagination_token,
            "sort": sort_order,
            "email": filter_by_email,
            "status": filter_by_status,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def invite_user_to_organization(
    context: ToolContext,
    organization_unique_identifier: Annotated[
        str, "The unique identifier for the organization to which the user is being invited."
    ],
    user_email: Annotated[str, "The email address of the user to be invited to the organization."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'create-organization-invitation'."]:
    """Invite a user to join an organization.

    This tool sends an invitation to a user to join a specific organization on Calendly. Use it when you need to add a new member to an organization by sending them an invitation."""  # noqa: E501
    request_data = remove_none_values({"email": user_email})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/organizations/{uuid}/invitations".format(  # noqa: UP032
            uuid=organization_unique_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def revoke_organization_invitation(
    context: ToolContext,
    invitation_unique_identifier: Annotated[
        str, "The unique identifier for the organization invitation to be revoked."
    ],
    organization_unique_identifier: Annotated[
        str, "The organization's unique identifier required to revoke the invitation."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'revoke-organization-invitation'."]:
    """Revoke an organization invitation in Calendly.

    Use this tool to revoke an organization invitation in Calendly, rendering the invitation link invalid."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/organizations/{org_uuid}/invitations/{uuid}".format(  # noqa: UP032
            org_uuid=organization_unique_identifier, uuid=invitation_unique_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_organization_invitation(
    context: ToolContext,
    organization_invitation_uuid: Annotated[
        str,
        "The unique identifier of the organization's invitation. Provide this to retrieve specific invitation details.",  # noqa: E501
    ],
    organization_unique_id: Annotated[str, "The unique identifier for the organization."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-organization-invitation'."]:
    """Fetches details of an organization's invitation.

    Use this tool to retrieve information about an invitation sent within an organization to its members. This can help track invitation status and details."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/organizations/{org_uuid}/invitations/{uuid}".format(  # noqa: UP032
            org_uuid=organization_unique_id, uuid=organization_invitation_uuid
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_organization_membership_info(
    context: ToolContext,
    organization_membership_uuid: Annotated[
        str,
        "The unique identifier for the organization's membership to retrieve details for a user.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-organization-membership'."]:
    """Retrieve details about a user's organization membership in Calendly.

    Use this tool to get information about a specific user's membership within an organization on Calendly. This tool can be called when you need detailed membership data for a given user, identified by their unique UUID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/organization_memberships/{uuid}".format(  # noqa: UP032
            uuid=organization_membership_uuid
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def remove_user_from_organization(
    context: ToolContext,
    organization_membership_unique_id: Annotated[
        str, "The unique identifier for the organization membership to be removed."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'delete-organization-membership'."]:
    """Remove a user from an organization with admin rights.

    This tool removes a user from an organization in Calendly. It requires the caller to have admin rights and cannot be used to remove an organization owner."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/organization_memberships/{uuid}".format(  # noqa: UP032
            uuid=organization_membership_unique_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_organization_memberships(
    context: ToolContext,
    filter_by_email: Annotated[
        str | None,
        "A specific email address to filter the organization memberships by. Only memberships associated with this email will be returned.",  # noqa: E501
    ] = None,
    filter_by_organization: Annotated[
        str | None,
        "Filter the results by organization. Provide the organization's unique identifier or name to retrieve specific memberships.",  # noqa: E501
    ] = None,
    filter_by_role: Annotated[
        str | None, "Filter the results by role. Options: 'owner', 'admin', 'user'."
    ] = None,
    filter_by_user: Annotated[
        str | None,
        "Filter the results by a specific user. Provide the user's identifier to narrow the search.",  # noqa: E501
    ] = None,
    next_page_token: Annotated[
        str | None, "Token used to retrieve the next or previous set of results for paginated data."
    ] = None,
    number_of_rows_to_return: Annotated[
        float | None, "Specify the number of rows to return in the response."
    ] = 20,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-organization-memberships'."]:
    """Retrieve organization memberships and related details.

    Use this tool to list the organization memberships for all users in an organization. You can also retrieve your organization's URI using this endpoint."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/organization_memberships",
        method="GET",
        params=remove_none_values({
            "page_token": next_page_token,
            "count": number_of_rows_to_return,
            "email": filter_by_email,
            "organization": filter_by_organization,
            "user": filter_by_user,
            "role": filter_by_role,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def create_webhook_subscription(
    context: ToolContext,
    callback_url: Annotated[
        str, "The endpoint URL to receive POST requests for subscribed events in Calendly."
    ],
    event_subscriptions: Annotated[
        list[str],
        "List of user events to subscribe to. Examples include 'invitee.created', 'invitee.canceled', etc.",  # noqa: E501
    ],
    organization_reference: Annotated[
        str, "The unique reference identifier for the organization associated with the webhook."
    ],
    webhook_subscription_scope: Annotated[
        str, 'Specifies the scope of the webhook subscription: "organization", "user", or "group".'
    ],
    group_reference: Annotated[
        str | None, "The unique reference to the group that the webhook will be tied to."
    ] = None,
    user_reference: Annotated[
        str | None, "The unique reference or ID of the user for whom the webhook will be tied."
    ] = None,
    webhook_signing_key: Annotated[
        str | None,
        "Optional secret key shared between your application and Calendly for verifying webhook signatures. Useful for ensuring webhook messages' authenticity.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createwebhooks'."]:
    """Create a webhook subscription for events in Calendly.

    Use this tool to set up a webhook subscription for either an organization, user, or group. It triggers on specified events like invitee creation or cancellation. Useful for integrating Calendly events with other applications or systems."""  # noqa: E501
    request_data = remove_none_values({
        "url": callback_url,
        "events": event_subscriptions,
        "organization": organization_reference,
        "user": user_reference,
        "group": group_reference,
        "scope": webhook_subscription_scope,
        "signing_key": webhook_signing_key,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/webhook_subscriptions",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_webhook_subscriptions(
    context: ToolContext,
    filter_scope: Annotated[
        str,
        "Filter the list by organization, user, or group. Acceptable values are 'organization', 'user', or 'group'.",  # noqa: E501
    ],
    organization_id: Annotated[
        str,
        "The ID of the organization that owns the subscriptions being returned. This field is always required.",  # noqa: E501
    ],
    filter_by_group: Annotated[
        str | None, "Optional; filters the results by group when scope is set to 'group'."
    ] = None,
    filter_by_user: Annotated[
        str | None, "Filter results by user when 'scope' is set to 'user'."
    ] = None,
    number_of_rows_to_return: Annotated[
        float | None, "Specify the number of rows to be returned in the result set."
    ] = 20,
    pagination_token: Annotated[
        str | None, "The token to retrieve the next or previous portion of the collection."
    ] = None,
    sort_by_field_and_direction: Annotated[
        str | None,
        "Specify the field and direction to order results. Use 'created_at:asc' or 'created_at:desc'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-webhook-subscriptions'."]:
    """Retrieve webhook subscriptions for an organization or user.

    Use this tool to obtain a list of webhook subscriptions associated with a specified organization or user on Calendly."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/webhook_subscriptions",
        method="GET",
        params=remove_none_values({
            "organization": organization_id,
            "user": filter_by_user,
            "group": filter_by_group,
            "page_token": pagination_token,
            "count": number_of_rows_to_return,
            "sort": sort_by_field_and_direction,
            "scope": filter_scope,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_webhook_subscription(
    context: ToolContext,
    webhook_identifier: Annotated[
        str, "The unique identifier of the webhook subscription to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-webhook-subscription'."]:
    """Retrieve details of a specific webhook subscription.

    Use this tool to get detailed information about a specific webhook subscription by providing its unique identifier."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/webhook_subscriptions/{webhook_uuid}".format(  # noqa: UP032
            webhook_uuid=webhook_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def delete_webhook_subscription(
    context: ToolContext,
    webhook_uuid: Annotated[
        str,
        "The unique identifier for the webhook subscription to be deleted. This is required to specify which subscription will be removed.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'delete-webhook-subscription'."]:
    """Delete a webhook subscription on Calendly.

    Use this tool to delete a specific webhook subscription from Calendly by providing the webhook UUID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/webhook_subscriptions/{webhook_uuid}".format(  # noqa: UP032
            webhook_uuid=webhook_uuid
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def create_scheduling_link(
    context: ToolContext,
    maximum_event_count: Annotated[
        float,
        "The maximum number of events that can be scheduled using this link. Currently, only '1' is supported.",  # noqa: E501
    ],
    resource_owner_link: Annotated[
        str, "A link to the resource owning this scheduling link, typically an Event Type URL."
    ],
    resource_type: Annotated[
        str, "Resource type for the scheduling link. This is always 'EventType'."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'create-scheduling-link'."]:
    """Creates a single-use scheduling link for appointments."""
    request_data = remove_none_values({
        "max_event_count": maximum_event_count,
        "owner": resource_owner_link,
        "owner_type": resource_type,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/scheduling_links",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def delete_invitee_data(
    context: ToolContext,
    invitee_email_list: Annotated[
        list[str],
        "A list of invitee emails to remove data for from all booked events. Each entry should be a valid email address.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'delete-invitee-data'."]:
    """Request removal of invitee data from all booked events.

    Use this tool to submit a request for deleting an invitee's data from all previously booked events within your organization. It requires an Enterprise subscription and can take up to 7 days to complete."""  # noqa: E501
    request_data = remove_none_values({"emails": invitee_email_list})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/data_compliance/deletion/invitees",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_invitee_no_show_details(
    context: ToolContext,
    invitee_uuid: Annotated[
        str, "The unique identifier for the invitee whose no-show information is being requested."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-invitee-no-show'."]:
    """Fetch details of a specified invitee no-show.

    Use this tool to retrieve detailed information about an invitee who did not show up for a scheduled event. This is helpful for managing scheduling follow-ups or analyzing attendance patterns."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/invitee_no_shows/{uuid}".format(uuid=invitee_uuid),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def undo_invitee_no_show_status(
    context: ToolContext,
    invitee_unique_id: Annotated[
        str, "The unique identifier for the invitee whose no-show status is to be undone."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'delete-invitee-no-show'."]:
    """Undo the no-show status for a Calendly invitee.

    This tool removes the no-show status previously assigned to an invitee in Calendly. Use it to correct any erroneous no-show markings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/invitee_no_shows/{uuid}".format(uuid=invitee_unique_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def mark_invitee_no_show(
    context: ToolContext,
    invitee_id: Annotated[
        str | None,
        "The unique identifier for the invitee to be marked as a no show. This is required to specify which invitee did not attend.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'create-invitee-no-show'."]:
    """Mark an invitee as a no show in Calendly.

    Use this tool to mark an invitee as a no show in Calendly. It should be called when you need to record that an invitee did not attend a scheduled event."""  # noqa: E501
    request_data = remove_none_values({"invitee": invitee_id})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/invitee_no_shows",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_group_info(
    context: ToolContext,
    group_unique_identifier: Annotated[
        str, "A unique identifier for the group whose information is to be retrieved from Calendly."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-group'."]:
    """Retrieve information about a specified group in Calendly.

    Use this tool to get detailed information about a specific group in Calendly by providing the group's UUID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/groups/{uuid}".format(uuid=group_unique_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_group_relationships(
    context: ToolContext,
    filter_by_group: Annotated[
        str | None, "Filter results by a specific group using a group identifier."
    ] = None,
    filter_by_organization: Annotated[
        str | None,
        "Filter results by organization. Provide the organization ID or URI to narrow down the list of group relationships.",  # noqa: E501
    ] = None,
    filter_by_owner_uri: Annotated[
        str | None,
        "The URI to filter results by owner, either an Organization Membership URI or Organization Invitation URI.",  # noqa: E501
    ] = None,
    number_of_rows: Annotated[
        float | None, "Specify the number of rows to return in the response."
    ] = 20,
    pagination_token: Annotated[
        str | None, "Token to navigate to the next or previous portion of the collection."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-group-relationships'."]:
    """Retrieve a list of group relationships for a given owner.

    Use this tool to get group relationship records, including one membership and multiple admin records for an owner."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/group_relationships",
        method="GET",
        params=remove_none_values({
            "count": number_of_rows,
            "page_token": pagination_token,
            "organization": filter_by_organization,
            "owner": filter_by_owner_uri,
            "group": filter_by_group,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_group_relationship_by_uuid(
    context: ToolContext,
    group_relationship_uuid: Annotated[
        str,
        "The unique identifier (UUID) of the group relationship to retrieve details for. Use this to specify which relationship to fetch.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-group-relationship'."]:
    """Retrieve group relationship details using a UUID.

    This tool fetches the details of a group relationship in Calendly by using the unique identifier (UUID). Use it when you need to access specific group relationship information."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/group_relationships/{uuid}".format(  # noqa: UP032
            uuid=group_relationship_uuid
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_organization_details(
    context: ToolContext,
    organization_uuid: Annotated[
        str, "The unique identifier for the organization to retrieve details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-organization'."]:
    """Retrieve details of a specified organization using UUID.

    Call this tool to obtain comprehensive information about an organization by providing its unique UUID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/organizations/{uuid}".format(uuid=organization_uuid),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def cancel_scheduled_event(
    context: ToolContext,
    event_unique_identifier: Annotated[str, "The unique identifier for the event to be canceled."],
    cancellation_reason: Annotated[
        str | None, "The reason for canceling the event. Provide a clear and concise explanation."
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'create-scheduled-event-cancellation'."
]:
    """Cancels a specified scheduled event on Calendly.

    Use this tool to cancel a scheduled event by providing its unique identifier. It should be called when you need to remove an event from your calendar."""  # noqa: E501
    request_data = remove_none_values({"reason": cancellation_reason})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/scheduled_events/{uuid}/cancellation".format(  # noqa: UP032
            uuid=event_unique_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_routing_forms(
    context: ToolContext,
    organization_uri: Annotated[
        str,
        "The URI of the organization to view its routing forms. It should be a valid string representing the organization's endpoint.",  # noqa: E501
    ],
    number_of_rows: Annotated[
        float | None, "The number of routing form entries to return for the request."
    ] = 20,
    pagination_token: Annotated[
        str | None,
        "Token for fetching the next or previous portion of the routing forms collection.",
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify the order of results using field and direction. Supports 'created_at' with 'asc' or 'desc'. Use comma for multiple fields.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-routing-forms'."]:
    """Retrieve routing forms for a specified organization.

    Use this tool to obtain a list of all routing forms associated with a specific organization. It should be called when there is a need to access routing forms data for organizational purposes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/routing_forms",
        method="GET",
        params=remove_none_values({
            "organization": organization_uri,
            "count": number_of_rows,
            "page_token": pagination_token,
            "sort": sort_order,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_routing_form(
    context: ToolContext,
    routing_form_uuid: Annotated[str, "A unique identifier for the routing form to be retrieved."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-routing-form'."]:
    """Retrieve details of a specified routing form.

    Call this tool to obtain information about a specific routing form using its UUID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/routing_forms/{uuid}".format(uuid=routing_form_uuid),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_routing_form_submissions(
    context: ToolContext,
    routing_form_uri: Annotated[
        str,
        "The URI of the routing form to view its submissions. This specifies which form's submissions to retrieve.",  # noqa: E501
    ],
    number_of_rows_to_return: Annotated[
        float | None, "Specify the number of routing form submissions to return."
    ] = 20,
    pagination_token: Annotated[
        str | None, "Token for retrieving the next or previous set of form submissions."
    ] = None,
    sort_order: Annotated[
        str | None, "Specify field and direction to sort results (e.g., 'created_at:asc')."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-routing-form-submissions'."]:
    """Get a list of Routing Form Submissions for a specified form.

    Use this tool to retrieve submissions from a specific routing form in your Calendly account. Ideal for accessing user responses collected through these forms."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/routing_form_submissions",
        method="GET",
        params=remove_none_values({
            "form": routing_form_uri,
            "count": number_of_rows_to_return,
            "page_token": pagination_token,
            "sort": sort_order,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_routing_form_submission(
    context: ToolContext,
    submission_uuid: Annotated[
        str, "Unique identifier for the routing form submission to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-routing-form-submission'."]:
    """Retrieve a specified Routing Form Submission by UUID.

    This tool retrieves information about a specific routing form submission identified by its unique UUID. Use it to access details of a specific form submission in Calendly."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/routing_form_submissions/{uuid}".format(uuid=submission_uuid),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_available_event_times(
    context: ToolContext,
    availability_end_time: Annotated[
        str, "End time for the availability range, must be after the start time."
    ],
    availability_start_time: Annotated[
        str, "The start time for the availability range. Must be a future date, not in the past."
    ],
    event_type_uri: Annotated[
        str, "The URI associated with the event type to retrieve its available times."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-event-type-available-times'."]:
    """Retrieve available times for an event type within a date range.

    Returns available times for a specified event type within a maximum 7-day date range. Useful for scheduling and planning events."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/event_type_available_times",
        method="GET",
        params=remove_none_values({
            "event_type": event_type_uri,
            "start_time": availability_start_time,
            "end_time": availability_end_time,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_activity_log_entries(
    context: ToolContext,
    organization_uri: Annotated[str, "URI of the organization to filter activity log entries."],
    actions: Annotated[
        list[str] | None,
        "Specify one or more actions associated with the log entries. Accepts an array of strings.",
    ] = None,
    entry_categories: Annotated[
        list[str] | None,
        "Specify the categories of log entries to filter the results. This is an array of strings, each representing a category.",  # noqa: E501
    ] = None,
    filter_by_search_term: Annotated[
        str | None,
        'Filters entries using supported operators: `|`, `+`, `"`, `-`, `()`, `*`. For example, `this | that` or `(email) + (signup)`.',  # noqa: E501
    ] = None,
    include_entries_after: Annotated[
        str | None,
        'Include entries that occurred after this time. Use format: "YYYY-MM-DDTHH:MM:SS.sssZ" (UTC).',  # noqa: E501
    ] = None,
    max_occurred_at_time: Annotated[
        str | None,
        'Include entries that occurred prior to this UTC time in the format "YYYY-MM-DDTHH:MM:SS.SSSZ".',  # noqa: E501
    ] = None,
    number_of_rows_to_return: Annotated[
        int | None, "Specifies the number of activity log entries to return in the response."
    ] = 20,
    pagination_token: Annotated[
        str | None, "Token to get the next portion of the activity log collection."
    ] = None,
    sort_order: Annotated[
        list[str] | None,
        "Specify the field and direction to sort results. Use format {field}:{direction}.",
    ] = None,
    user_associated_uris: Annotated[
        list[str] | None,
        "Return entries from the user(s) associated with the provided URIs. This should be an array of strings representing the URIs of users.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-activity-log-entries'."]:
    """Fetch a list of activity log entries.

    This tool retrieves a list of activity log entries from Calendly, requiring an Enterprise subscription. It is useful for accessing detailed logs of various activities."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/activity_log_entries",
        method="GET",
        params=remove_none_values({
            "organization": organization_uri,
            "search_term": filter_by_search_term,
            "actor": user_associated_uris,
            "sort": sort_order,
            "min_occurred_at": include_entries_after,
            "max_occurred_at": max_occurred_at_time,
            "page_token": pagination_token,
            "count": number_of_rows_to_return,
            "namespace": entry_categories,
            "action": actions,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def create_custom_share_link(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'create-share'."]:
    """Create a shareable link for a customized event.

    Use this tool to generate a customized shareable link for one-on-one event types on Calendly. This tool should be called when you want to tailor event details for a specific invitee without creating a new event type. Note: Any details not specified will be inherited from the existing event.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATECUSTOMSHARELINK"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["CREATECUSTOMSHARELINK"] + "\n```"
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["CREATECUSTOMSHARELINK"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.calendly.com/shares",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATECUSTOMSHARELINK"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def list_user_busy_times(
    context: ToolContext,
    availability_start_time: Annotated[
        str, "Start time for the availability range. The date must not be in the past."
    ],
    end_time: Annotated[
        str, "End time for the requested availability range, must be after the start_time."
    ],
    user_uri: Annotated[str, "The URI associated with the user to retrieve busy times for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-user-busy-times'."]:
    """Retrieve user's scheduled events within a specific date range.

    Use this tool to obtain an ascending list of all internal and external events scheduled for a user within a specified date range, up to 7 days. External events are returned only if calendars are set to \"Check for conflicts.\" This tool is helpful for checking availability or scheduling new events based on the user's current commitments."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/user_busy_times",
        method="GET",
        params=remove_none_values({
            "user": user_uri,
            "start_time": availability_start_time,
            "end_time": end_time,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_user_availability_schedules(
    context: ToolContext,
    user_uri_reference: Annotated[
        str,
        "A URI reference to the specified user whose availability schedules are to be retrieved.",
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'list-user-availability-schedules'."
]:
    """Fetch a user's availability schedules.

    Use this tool to get the availability schedules of a specified user, assisting in planning or checking user availability."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/user_availability_schedules",
        method="GET",
        params=remove_none_values({"user": user_uri_reference}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_user_availability_schedule(
    context: ToolContext,
    user_uuid: Annotated[str, "The UUID of the availability schedule you want to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-user-availability-schedule'."]:
    """Retrieve a user's availability schedule using their UUID.

    Use this tool to obtain the availability schedule for a specific user identified by their UUID. It provides the details of available time slots."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/user_availability_schedules/{uuid}".format(uuid=user_uuid),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def delete_scheduled_events_data(
    context: ToolContext,
    deletion_start_time_utc: Annotated[
        str,
        "The UTC timestamp to start deleting scheduled events data. Must be in the past and not older than 24 months.",  # noqa: E501
    ],
    end_time_utc: Annotated[
        str,
        "The UTC timestamp marking the end of the time range for data deletion, in the past, no greater than 24 months ago.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'delete-scheduled-event-data'."]:
    """Delete scheduled events data within a past time range.

    Use this tool to request the deletion of scheduled events data for your organization within a specified past time range, up to 24 months. Completion may take up to 7 days. Requires an Enterprise subscription."""  # noqa: E501
    request_data = remove_none_values({
        "start_time": deletion_start_time_utc,
        "end_time": end_time_utc,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/data_compliance/deletion/events",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def fetch_event_type_hosts(
    context: ToolContext,
    event_type_uri: Annotated[str, "The URI associated with the event type to identify the hosts."],
    number_of_rows_to_return: Annotated[
        float | None,
        "Specify the number of rows to fetch from the list of event type hosts. This determines the size of the dataset returned.",  # noqa: E501
    ] = 20,
    pagination_token: Annotated[
        str | None, "Token for fetching the next or previous portion of the event type host list."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-event-type-memberships'."]:
    """Fetches a list of event type hosts from Calendly.

    Use this tool to retrieve a list of hosts for event types on Calendly. This can be useful to determine availability or to manage event type assignments."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/event_type_memberships",
        method="GET",
        params=remove_none_values({
            "event_type": event_type_uri,
            "count": number_of_rows_to_return,
            "page_token": pagination_token,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def create_one_off_event(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'create-one-off-event-type'."]:
    """Create a one-off event type in Calendly.

    This tool is used to create a one-off event type in Calendly, allowing users to schedule unique events not linked to recurring event types.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEONEOFFEVENT"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["CREATEONEOFFEVENT"] + "\n```"
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["CREATEONEOFFEVENT"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.calendly.com/one_off_event_types",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEONEOFFEVENT"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_sample_webhook_data(
    context: ToolContext,
    organization_identifier: Annotated[
        str,
        "The unique identifier for the organization. It specifies which organization the sample webhook data belongs to.",  # noqa: E501
    ],
    webhook_event_type: Annotated[
        str,
        "Specify the type of webhook event to simulate, such as 'invitee.created' or 'invitee.canceled'.",  # noqa: E501
    ],
    webhook_scope: Annotated[
        str,
        "Specify the scope for the sample webhook data. Options are 'user', 'organization', or 'group'.",  # noqa: E501
    ],
    user_identifier: Annotated[
        str | None,
        "The unique identifier for a user in Calendly whose webhook data you want to test.",
    ] = None,
    webhook_event_group: Annotated[
        str | None,
        "Specify the group for the webhook event to categorize and filter data. Typically used for organizing related webhooks.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-sample-webhook-data'."]:
    """Retrieve sample webhook data for testing integrations.

    Use this tool to obtain a sample webhook payload from Calendly for testing your webhook subscription integration. This is useful for developers who want to ensure their systems can properly handle the data structure and content sent by Calendly webhooks before going live."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/sample_webhook_data",
        method="GET",
        params=remove_none_values({
            "event": webhook_event_type,
            "organization": organization_identifier,
            "user": user_identifier,
            "scope": webhook_scope,
            "group": webhook_event_group,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def fetch_outgoing_communications(
    context: ToolContext,
    organization_uri: Annotated[
        str,
        "Return outgoing communications from the organization associated with this URI. This should be a valid URI string.",  # noqa: E501
    ],
    created_before: Annotated[
        str | None,
        'Include outgoing communications created before this time in UTC format (e.g., "2020-01-02T03:04:05.678Z").',  # noqa: E501
    ] = None,
    number_of_records_to_return: Annotated[
        int | None, "The maximum number of outgoing communications records to retrieve."
    ] = 20,
    pagination_token: Annotated[
        str | None, "Token for fetching the next set of outgoing communications."
    ] = None,
    start_time_utc: Annotated[
        str | None,
        'Include communications created after this UTC time (e.g. "2020-01-02T03:04:05.678Z").',
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-outgoing-communications'."]:
    """Retrieve outgoing SMS and email communications.

    Fetches a list of outgoing SMS and email communications for users with an Enterprise subscription."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/outgoing_communications",
        method="GET",
        params=remove_none_values({
            "organization": organization_uri,
            "count": number_of_records_to_return,
            "min_created_at": start_time_utc,
            "max_created_at": created_before,
            "page_token": pagination_token,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_group_list(
    context: ToolContext,
    organization_uri: Annotated[
        str, "URI for the organization to return associated groups from Calendly."
    ],
    number_of_rows: Annotated[
        float | None,
        "Specify the number of rows (groups) to return from the query. Used to limit results.",
    ] = 20,
    pagination_token: Annotated[
        str | None, "Token for retrieving the next or previous set of groups in the list."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-groups'."]:
    """Retrieve a list of groups from Calendly.

    Use this tool to obtain information about the various groups available in Calendly. It returns a list detailing these groups."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/groups",
        method="GET",
        params=remove_none_values({
            "organization": organization_uri,
            "page_token": pagination_token,
            "count": number_of_rows,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_user_location_info(
    context: ToolContext,
    user_uri: Annotated[
        str, "The URI identifying the specific user to retrieve location information for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'list-user-locations'."]:
    """Retrieve configured location details for a specific user.

    This tool is used to obtain the location information that a user has configured in their account. It can be called when location details are needed for scheduling or meeting purposes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/locations",
        method="GET",
        params=remove_none_values({"user": user_uri}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def get_event_availability(
    context: ToolContext,
    event_type_uri: Annotated[
        str, "The URI associated with the specific event type to retrieve availability."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-event-type-availability'."]:
    """Retrieve availability for a specific event type.

    This tool returns the availability schedule for a given event type. It should be called when you need to check open time slots for booking or planning purposes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.calendly.com/event_type_availability_schedules",
        method="GET",
        params=remove_none_values({"event_type": event_type_uri}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-calendly"))
async def update_event_availability(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    event_type_uri: Annotated[
        str | None,
        "URI of the event type to update the availability schedule for.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'update-event-type-availability'."]:
    """Update an event type availability schedule.

    Use this tool to modify the availability schedule of an existing calendar event type, such as updating times or days available.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEEVENTAVAILABILITY"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not event_type_uri:
        missing_params.append(("event_type_uri", "query"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["UPDATEEVENTAVAILABILITY"] + "\n```"
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n```json\n" + REQUEST_BODY_SCHEMAS["UPDATEEVENTAVAILABILITY"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.calendly.com/event_type_availability_schedules",
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEEVENTAVAILABILITY"],
        params=remove_none_values({"event_type": event_type_uri}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}
