<?php
/**
 * Abstract Message Base Class.
 *
 * Base class for all message types in the chat system. Provides common
 * functionality for message handling and formatting.
 *
 * @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://agenistrator.com/
 *
 * 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' ) ) {
	exit;
}

/**
 * Message class.
 */
abstract class Message {
	/**
	 * Message ID from database (null for unsaved messages)
	 * @since     1.0.0
	 */
	protected $message_id;

	/**
	 * Message role (user, assistant, system, tool_call, tool_result)
	 * @since     1.0.0
	 */
	protected $role;

	/**
	 * Human-readable message content (null only for tool call/result messages)
	 * @since     1.0.0
	 */
	protected $content;

	/**
	 * Structured metadata (JSON-serializable)
	 * @since     1.0.0
	 */
	protected $metadata;

	/**
	 * Message timestamp
	 * @since     1.0.0
	 */
	protected $timestamp;

	/**
	 * Constructor
	 *
	 * @param string|null $content Human-readable message content (null only for tool call/result messages)
	 * @param array|null $metadata Optional structured metadata
	 * @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( $content, $metadata = null, $timestamp = null, $message_id = null ) {
		$this->content    = $content;
		$this->metadata   = $metadata;
		$this->timestamp  = $timestamp ?? time();
		$this->message_id = $message_id;
		$this->role       = $this->get_role();
	}

	/**
	 * Get the message role (implemented by subclasses)
	 *
	 * @return string The message role
	 * @since     1.0.0
	 */
	abstract public function get_role();

	/**
	 * Get message ID
	 *
	 * @return int|null Message ID or null if not saved
	 * @since     1.0.0
	 */
	public function get_message_id() {
		return $this->message_id;
	}

	/**
	 * Set message ID (used when saving to database)
	 *
	 * @param int $message_id The message ID
	 * @return Message This instance for method chaining
	 * @since     1.0.0
	 */
	public function set_message_id( $message_id ) {
		$this->message_id = $message_id;
		return $this;
	}

	/**
	 * Get message content
	 *
	 * @return string|null The message content (null only for tool call/result messages)
	 * @since     1.0.0
	 */
	public function get_content() {
		return $this->content;
	}

	/**
	 * Set message content
	 *
	 * @param string|null $content The message content (null only for tool call/result messages)
	 * @return Message This instance for method chaining
	 * @since     1.0.0
	 */
	public function set_content( $content ) {
		$this->content = $content;
		return $this;
	}

	/**
	 * Get message metadata
	 *
	 * @return array|null The message metadata
	 * @since     1.0.0
	 */
	public function get_metadata() {
		return $this->metadata;
	}

	/**
	 * Set message metadata
	 *
	 * @param array|null $metadata The message metadata
	 * @return Message This instance for method chaining
	 * @since     1.0.0
	 */
	public function set_metadata( $metadata ) {
		$this->metadata = $metadata;
		return $this;
	}

	/**
	 * Get message timestamp
	 *
	 * @return int The message timestamp
	 * @since     1.0.0
	 */
	public function get_timestamp() {
		return $this->timestamp;
	}

	/**
	 * Set message timestamp
	 *
	 * @param int $timestamp The message timestamp
	 * @return Message This instance for method chaining
	 * @since     1.0.0
	 */
	public function set_timestamp( $timestamp ) {
		$this->timestamp = $timestamp;
		return $this;
	}

	/**
	 * Check if this message has been saved to database
	 *
	 * @return bool True if saved, false otherwise
	 * @since     1.0.0
	 */
	public function is_saved() {
		return null !== $this->message_id;
	}

	/**
	 * Convert message to array format for database storage
	 *
	 * @return array Message data array
	 * @since     1.0.0
	 */
	public function to_array() {
		$data = array(
			'role'      => $this->get_role(),
			'content'   => $this->content,
			'metadata'  => $this->metadata,
			'timestamp' => $this->timestamp,
		);

		if ( null !== $this->message_id ) {
			$data['message_id'] = $this->message_id;
		}

		return $data;
	}

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

		// Delegate to appropriate subclass factory method
		switch ( $data['role'] ) {
			case 'system':
				return System_Message::from_array( $data );
			case 'user':
				return User_Message::from_array( $data );
			case 'assistant':
				return Assistant_Message::from_array( $data );
			case 'tool_call':
				return Tool_Call_Message::from_array( $data );
			case 'tool_result':
				return Tool_Result_Message::from_array( $data );
			default:
				return null;
		}
	}

	/**
	 * Convert message to JSON string
	 *
	 * @return string JSON representation
	 * @since     1.0.0
	 */
	public function to_json() {
		return wp_json_encode( $this->to_array() );
	}

	/**
	 * Create message instance from JSON string
	 *
	 * @param string $json JSON string
	 * @return Message|null Message instance or null if invalid
	 * @since     1.0.0
	 */
	public static function from_json( $json ) {
		$data = json_decode( $json, true );
		if ( ! is_array( $data ) ) {
			return null;
		}
		return self::from_array( $data );
	}
}
