<?php
/**
 * Tool Call Message Class.
 *
 * Represents programmatic tool call requests in the conversation. This is a
 * structured data container that carries tool metadata. The actual content for
 * LLM providers is generated from the metadata.
 *
 * @package   Agenistrator
 * @since     1.0.0
 * @author    Alex Ruban <alex@maximum.guru>
 * @author    Maximum Software Inc <contact@maximum.software>
 * @license   GPL-3.0-only
 * @link      https://maximum.software/
 *
 * SPDX-FileCopyrightText: 2025-2026 Alex Ruban <alex@maximum.guru>
 * SPDX-FileCopyrightText: 2025-2026 Maximum Software Inc <contact@maximum.software>
 * SPDX-License-Identifier: GPL-3.0-only
 */

namespace Agenistrator;

// Direct access prevention
if ( ! defined( 'ABSPATH' ) ) {
	return;
}

/**
 * Tool_Call_Message class.
 */
class Tool_Call_Message extends Message {
	/**
	 * Constructor
	 *
	 * @param string $tool_id The ID of the tool being called
	 * @param array $arguments The tool arguments as an associative array
	 * @param string $call_id Required call ID from LLM provider
	 * @param int|null $timestamp Optional timestamp (defaults to current time)
	 * @param int|null $message_id Optional message ID from database
	 * @since     1.0.0
	 */
	public function __construct( $tool_id, $arguments, $call_id, $timestamp = null, $message_id = null ) {
		if ( empty( $call_id ) ) {
			$tool      = Tool_Manager::get_instance()->get_tool( $tool_id );
			$tool_name = $tool ? $tool->get_name() : $tool_id;
			// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Exception messages are not HTML output
			throw new \InvalidArgumentException(
				String_Utils::replace_tags(
					\__( "Tool '{tool_name}': Tool call ID is required and must come from LLM provider", 'agenistrator' ),
					array( 'tool_name' => $tool_name )
				)
			);
			// phpcs:enable WordPress.Security.EscapeOutput.ExceptionNotEscaped
		}

		$metadata = array(
			'tool_id'   => $tool_id,
			'arguments' => $arguments,
			'call_id'   => $call_id,
		);

		// Tool calls are programmatic - no human-readable content needed
		parent::__construct( null, $metadata, $timestamp, $message_id );
	}

	/**
	 * Create Tool_Call_Message instance from array data
	 *
	 * @param array $data Message data array
	 * @return Tool_Call_Message|null Message instance or null if invalid
	 * @since     1.0.0
	 */
	public static function from_array( $data ) {
		if ( ! isset( $data['metadata'] ) || ! is_array( $data['metadata'] ) ) {
			return null;
		}

		$metadata = $data['metadata'];

		$instance = new self(
			$metadata['tool_id'] ?? 'unknown_tool',
			$metadata['arguments'] ?? array(),
			$metadata['call_id'] ?? null,
			$data['timestamp'] ?? time(),
			$data['message_id'] ?? null
		);

		// Restore full metadata (preserves token_count and other cached fields)
		$instance->set_metadata( $metadata );

		return $instance;
	}

	/**
	 * Get the message role
	 *
	 * @return string The message role
	 * @since     1.0.0
	 */
	public function get_role() {
		return 'tool_call';
	}

	/**
	 * Get the tool ID
	 *
	 * @return string The tool ID
	 * @since     1.0.0
	 */
	public function get_tool_id() {
		return $this->metadata['tool_id'] ?? 'unknown_tool';
	}

	/**
	 * Get the tool arguments
	 *
	 * @return array The tool arguments as an associative array
	 * @since     1.0.0
	 */
	public function get_arguments() {
		return $this->metadata['arguments'] ?? array();
	}

	/**
	 * Get the call ID
	 *
	 * @return string The call ID
	 * @since     1.0.0
	 */
	public function get_call_id() {
		return $this->metadata['call_id'];
	}

	/**
	 * Set the tool ID
	 *
	 * @param string $tool_id The tool ID
	 * @return Tool_Call_Message This instance for method chaining
	 * @since     1.0.0
	 */
	public function set_tool_id( $tool_id ) {
		if ( ! is_array( $this->metadata ) ) {
			$this->metadata = array();
		}
		$this->metadata['tool_id'] = $tool_id;
		return $this;
	}

	/**
	 * Set the tool arguments
	 *
	 * @param array $arguments The tool arguments as an associative array
	 * @return Tool_Call_Message This instance for method chaining
	 * @since     1.0.0
	 */
	public function set_arguments( $arguments ) {
		if ( ! is_array( $this->metadata ) ) {
			$this->metadata = array();
		}

		$this->metadata['arguments'] = $arguments;
		return $this;
	}

	/**
	 * Set the call ID
	 *
	 * @param string|null $call_id The call ID
	 * @return Tool_Call_Message This instance for method chaining
	 * @since     1.0.0
	 */
	public function set_call_id( $call_id ) {
		if ( ! is_array( $this->metadata ) ) {
			$this->metadata = array();
		}
		$this->metadata['call_id'] = $call_id;
		return $this;
	}
}
