<?php

namespace Limb_Chatbot\Includes\Services;

use Limb_Chatbot\Includes\Data_Objects\AI_Model;
use Limb_Chatbot\Includes\Data_Objects\Dataset;
use Limb_Chatbot\Includes\Data_Objects\Dataset_Entry;
use Limb_Chatbot\Includes\Repositories\Dataset_Repository;


/**
 * Abstract service for formatting fine-tuning dataset entries into a model-specific format.
 *
 * @since 1.0.0
 */
abstract class Fine_Tuning_File_Formatting_Service {

	/**
	 * Repository instance for accessing dataset records.
	 *
	 * @since 1.0.0
	 * @var Dataset_Repository
	 */
	protected Dataset_Repository $dataset_repository;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->dataset_repository = new Dataset_Repository();
	}

	/**
	 * Format a dataset entry into a fine-tuning-compatible string for a given model.
	 *
	 * @since 1.0.0
	 *
	 * @param Dataset_Entry $dataset_entry The dataset entry object.
	 * @param AI_Model      $model         The model the entry is being prepared for.
	 * @return string The formatted string.
	 */
	public function format( Dataset_Entry $dataset_entry, AI_Model $model ): string {
		$this->apply_variables( $dataset_entry, $model );

		return $this->prepare_message_row( $dataset_entry, Dataset::find( $dataset_entry->get_dataset_id() ) );
	}

	/**
	 * Abstract method that defines how a dataset entry should be formatted into a message row.
	 * Implementations should return a properly structured string for fine-tuning.
	 *
	 * @since 1.0.0
	 *
	 * @param Dataset_Entry $dataset_entry The entry to format.
	 * @param Dataset|null  $dataset       Optional dataset reference.
	 * @return string|null The formatted row or null on failure.
	 */
	abstract function prepare_message_row( Dataset_Entry $dataset_entry, ?Dataset $dataset = null ): ?string;


	/**
	 * Replaces template variables in the dataset entry using the AI model's variables.
	 *
	 * @since 1.0.0
	 *
	 * @param Dataset_Entry $dataset_entry The dataset entry to update.
	 * @param AI_Model      $ai_model      The AI model containing variable mappings.
	 * @return void
	 */
	private function apply_variables( Dataset_Entry $dataset_entry, AI_Model $ai_model ): void {
		$variables = $ai_model->get_meta( 'variables' );
		if ( ! empty( $variables ) && is_array( $variables ) ) {
			if ( $entry_json = ( $dataset_entry->get_entry() ? json_encode( $dataset_entry->get_entry(), JSON_UNESCAPED_UNICODE ) : null ) ) {
				$entry_json = preg_replace_callback( '/{{\s*(.*?)\s*}}/', function ( $matches ) use ( $variables ) {
					return array_key_exists( $matches[1], $variables ) ? $variables[ $matches[1] ] : $matches[0];
				}, $entry_json );
				$entry      = json_decode( $entry_json, true );
			} else {
				// Maybe throwing error will be ideal
			}
		}
		if ( isset( $entry ) && is_array( $entry ) ) {
			$dataset_entry->set_entry( $entry );
		}
	}
}