declare module 'rclnodejs' {
  type ActionGoal<T> = T extends ActionTypeClassName
    ? InstanceType<ActionsMap[T]['Goal']>
    : object;
  type ActionFeedback<T> = T extends ActionTypeClassName
    ? InstanceType<ActionsMap[T]['Feedback']>
    : object;
  type ActionResult<T> = T extends ActionTypeClassName
    ? InstanceType<ActionsMap[T]['Result']>
    : object;

  /**
   * Goal handle for working with Action Clients.
   */
  class ClientGoalHandle<T extends TypeClass<ActionTypeClassName>> {
    /**
     * Gets the goal Id.
     */
    get goalId(): unique_identifier_msgs.msg.UUID;

    /**
     * Gets the goal response timestamp.
     */
    get stamp(): builtin_interfaces.msg.Time;

    /**
     * Gets if the goal response was accepted.
     * @deprecated Use isAccepted()
     */
    get accepted(): boolean;

    /**
     * Determine if goal is currently executing
     * @returns {bool} - True if goal is executing; otherwise return false.
     */
    isAccepted(): boolean;

    /**
     * Determine if goal is currently executing
     * @returns {bool} - True if goal is executing; otherwise return false.
     */
    isExecuting(): boolean;

    /**
     * Determine if goal is in the process of canceling.
     * @returns True if goal is canceling; otherwise return false.
     */
    isCanceling(): boolean;

    /**
     * Determine if goal completed successfullly.
     * @returns True if goal completed successfully; otherwise return false.
     */
    isSucceeded(): boolean;

    /**
     * Determine if goal has been canceled.
     * @returns True if goal has been aborted; otherwise return false.
     */
    isCanceled(): boolean;

    /**
     * Determine if goal has been aborted.
     * @returns True if goal was aborted; otherwise return false.
     */
    isAborted(): boolean;

    /**
     * Gets the goal status.
     */
    get status(): number;

    /**
     * Send a cancel request for the goal.
     *
     * @returns The cancel response.
     */
    cancelGoal(): Promise<action_msgs.srv.CancelGoal_Response>;

    /**
     * Request the result for the goal.
     *
     * @returns The result response.
     */
    getResult(): Promise<ActionResult<T>>;
  }

  /**
   * ROS Middleware "quality of service" options for action clients.
   */
  interface ActionQoS {
    /**
     * Quality of service option for the goal service, default: QoS.profileServicesDefault.
     */
    goalServiceQosProfile?: QoS | QoS.ProfileRef;

    /**
     * Quality of service option for the result service, default: QoS.profileServicesDefault.
     */
    resultServiceQosProfile?: QoS | QoS.ProfileRef;

    /**
     * Quality of service option for the cancel service, default: QoS.profileServicesDefault.
     */
    cancelServiceQosProfile?: QoS | QoS.ProfileRef;

    /**
     * Quality of service option for the feedback subscription, default: new QoS(QoS.HistoryPolicy.RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT, 10).
     */
    feedbackSubQosProfile?: QoS | QoS.ProfileRef;

    /**
     * Quality of service option for the status subscription, default: QoS.profileActionStatusDefault.
     */
    statusSubQosProfile?: QoS | QoS.ProfileRef;
  }

  /**
   * Options for sending a goal
   */
  interface SendGoalOptions {
    /** Override validateGoals setting for this call */
    validate?: boolean;
  }

  /**
   * ROS Action client.
   */
  class ActionClient<T extends TypeClass<ActionTypeClassName>> {
    /**
     * Creates a new action client.
     *
     * @param node - The ROS node to add the action client to.
     * @param typeClass - Type of the action.
     * @param actionName - Name of the action.
     * @param options - The topic will use TypedArray if necessary, default: true.
     */
    constructor(
      node: Node,
      typeClass: T,
      actionName: string,
      options?: Options<ActionQoS> & {
        validateGoals?: boolean;
        validationOptions?: MessageValidationOptions;
        /**
         * Enable feedback subscription content filter to optimize the handling
         * of feedback messages. When enabled, the content filter is used to
         * configure the goal ID for the subscription, avoiding reception of
         * irrelevant feedback messages. An action client can handle up to 6
         * goals simultaneously with this optimization. Default: false.
         */
        enableFeedbackMsgOptimization?: boolean;
      }
    );

    /**
     * Whether goals will be validated before sending.
     */
    willValidateGoal: boolean;

    /**
     * Set validation options for this action client.
     * @param options - Validation options
     */
    setValidation(options: MessageValidationOptions): void;

    /**
     * Send a goal and wait for the goal ACK asynchronously.
     *
     * Return a Promise object that is resolved with a ClientGoalHandle when receipt of the goal
     * is acknowledged by an action server, see client state transition https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Actions/Understanding-ROS2-Actions.html
     *
     * @param goal - The goal request.
     * @param feedbackCallback - Callback function for feedback associated with the goal.
     * @param goalUuid - Universally unique identifier for the goal. If None, then a random UUID is generated.
     * @param options - Send options (e.g., { validate: true })
     * @returns A Promise to a goal handle that resolves when the goal request has been accepted or rejected.
     * @throws MessageValidationError if validation is enabled and goal is invalid
     */
    sendGoal(
      goal: ActionGoal<T>,
      feedbackCallback?: (feedbackMessage: ActionFeedback<T>) => void,
      goalUuid?: unique_identifier_msgs.msg.UUID,
      options?: SendGoalOptions
    ): Promise<ClientGoalHandle<T>>;

    /**
     * Check if there is an action server ready to process requests from this client.
     *
     * @returns True if an action server is ready; otherwise, false.
     */
    isActionServerAvailable(): boolean;

    /**
     * Wait until the action server is available or a timeout is reached. This
     * function polls for the server state so it may not return as soon as the
     * server is available.
     *
     * @param timeout The maximum amount of time to wait for, if timeout
     * is `undefined` or `< 0`, this will wait indefinitely.
     * @returns True if the service is available.
     */
    waitForServer(timeout?: number): Promise<boolean>;

    /**
     * Destroy the underlying action client handle.
     */
    destroy(): void;

    /**
     * Get the number of wait set entities that make up an action entity.
     * @return - The number of subscriptions, guard_conditions, timers, and clients and services.
     */
    getNumEntities(): object;

    /**
     * Configure introspection.
     * @param clock - Clock to use for service event timestamps
     * @param QoSProfile - QOS profile for the service event publisher
     * @param introspectionState - The state to set introspection to
     */
    configureIntrospection(
      clock: Clock,
      serviceEventPubQOS: QoS,
      introspectionState: ServiceIntrospectionStates
    ): void;
  }
}
