<?php

namespace Limb_Chatbot\Includes\Services\Actions;

use Limb_Chatbot\Includes\Data_Objects\Action;
use Limb_Chatbot\Includes\Data_Objects\Action_Plan;
use Limb_Chatbot\Includes\Data_Objects\Action_Plan_Step;
use Limb_Chatbot\Includes\Data_Objects\Message;
use Limb_Chatbot\Includes\Data_Objects\Parameter;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Stream_Event_Service;
use Limb_Chatbot\Includes\Utilities\Chatbot_Utility;
use Limb_Chatbot\Includes\Utilities\Copilot_Utility;

/**
 * Class Parameter_Request_Message_Builder
 *
 * Responsible for building parameter request messages.
 * Separates parameter message generation concerns from action service coordination.
 * Follows Single Responsibility Principle by handling only message creation logic.
 *
 * @package Limb_Chatbot\Includes\Services\Actions
 * @since 1.0.0
 */
class Parameter_Request_Message_Builder {

	/**
	 * Chatbot utility for AI-powered question generation
	 *
	 * @var Chatbot_Utility|null
	 */
	private ?Chatbot_Utility $chatbot_utility;

	/**
	 * Constructor with dependency injection
	 *
	 * @param Chatbot_Utility|null $chatbot_utility Optional chatbot utility for AI features
	 *
	 * @since 1.0.0
	 */
	public function __construct( ?Chatbot_Utility $chatbot_utility = null ) {
		$this->chatbot_utility = $chatbot_utility;
	}

	/**
	 * Build a parameter request message
	 *
	 * Creates a message asking the user to provide a value for a parameter.
	 * Uses pre-defined questions, AI-generated questions, or parameter labels.
	 *
	 * @param Action_Plan      $action_plan The action plan containing context
	 * @param Action_Plan_Step $step        The step with parameter data
	 *
	 * @return Message|null The parameter request message or null if parameter is invalid
	 * @since 1.0.0
	 */
	public function build( Action_Plan $action_plan, Action_Plan_Step $step, $repeat = false ): ?Message {
		$data      = $step->get_data();
		$parameter = $data['parameter'];

		if ( ! $parameter instanceof Parameter ) {
			return null;
		}

		$question = null;

		if ( $parameter->is_searchable() ) {
			$search_result_provider = new Search_Results_Provider( $this->chatbot_utility );
			$search_results         = $search_result_provider->get_from_last_message( $action_plan, $parameter );
			$parameter->inject_results( $search_results );

			// Check if this is the first step (no previous step) and parameter is searchable
			$question = $this->get_ai_question_for_searchable_parameter( $search_results, $action_plan, $step );
		}

		if ( empty( $question ) ) {
			$question = $this->determine_question( $parameter, $action_plan, $step, $repeat );
		}

		$parameter_list_context = $this->get_parameter_list_context( $action_plan, $step );

		return $this->create_message( $parameter, $question, $parameter_list_context );
	}

	/**
	 * Get context about the current parameter's position in the action plan parameter list.
	 *
	 * @param Action_Plan      $action_plan The action plan
	 * @param Action_Plan_Step $step        The current step
	 *
	 * @return array{total_count: int, current_order: int, next_parameter: Parameter|null, previous_parameter: Parameter|null}
	 * @since 1.0.0
	 */
	private function get_parameter_list_context( Action_Plan $action_plan, Action_Plan_Step $step ): array {
		$parameter_steps = [];
		foreach ( $action_plan->steps as $plan_step ) {
			if ( ! $plan_step instanceof Action_Plan_Step || ! $plan_step->is_data_collect_step() ) {
				continue;
			}
			$data = $plan_step->get_data();
			if ( ! isset( $data['parameter'] ) || ! $data['parameter'] instanceof Parameter ) {
				continue;
			}
			$parameter_steps[] = $plan_step;
		}

		$total_count   = count( $parameter_steps );
		$current_index = null;
		foreach ( $parameter_steps as $i => $parameter_step ) {
			if ( $parameter_step === $step ) {
				$current_index = $i;
				break;
			}
		}

		$current_order = $current_index !== null ? $current_index + 1 : 0;
		$next_step     = $current_index !== null && isset( $parameter_steps[ $current_index + 1 ] ) ? $parameter_steps[ $current_index + 1 ] : null;
		$previous_step = $current_index !== null && $current_index > 0 && isset( $parameter_steps[ $current_index - 1 ] ) ? $parameter_steps[ $current_index - 1 ] : null;

		return [
			'total_count'        => $total_count,
			'current_order'      => $current_order,
			'next_parameter'     => $next_step ? $next_step->get_data()['parameter'] : null,
			'previous_parameter' => $previous_step ? $previous_step->get_data()['parameter'] : null,
		];
	}

	/**
	 * Determine the question to ask for the parameter
	 *
	 * Priority order:
	 * 1. Pre-defined question in parameter
	 * 2. AI-generated question (if chatbot utility available)
	 * 3. Parameter label as fallback
	 *
	 * @param Parameter        $parameter  The parameter to ask about
	 * @param Action_Plan      $action_plan The action plan for context
	 * @param Action_Plan_Step $step       The step for context
	 *
	 * @return string The question to display to the user
	 * @since 1.0.0
	 */
	private function determine_question(
		Parameter $parameter,
		Action_Plan $action_plan,
		Action_Plan_Step $step,
		$repeat = false
	): string {
		// Use pre-defined question if available
		if ( ! empty( $parameter->get_question() ) ) {
			return $parameter->get_question();
		}

		// Generate AI-powered question if utility available
		if ( ! empty( $this->chatbot_utility ) ) {
			$question = $this->generate_ai_question( $action_plan, $step, $repeat );
			if ( ! empty( $question ) ) {
				return $question;
			}
		}

		// Fallback to parameter label
		return $parameter->get_label();
	}

	/**
	 * Generate an AI-powered question for the parameter
	 *
	 * @param Action_Plan      $action_plan The action plan for context
	 * @param Action_Plan_Step $step        The step for context
	 *
	 * @return string|null The generated question or null if generation fails
	 * @since 1.0.0
	 */
	private function generate_ai_question( Action_Plan $action_plan, Action_Plan_Step $step, $repeat = false ): ?string {
		try {
			$copilot  = new Copilot_Utility(
				$this->chatbot_utility->get_ai_model(),
				$this->chatbot_utility->get_config()
			);
			$question = $copilot->generate_data_collect_question( $action_plan, $step, $repeat, $this->chatbot_utility->get_stream() );

			if ( $question instanceof Message ) {
				return $question->extract_text();
			}

			return $question;
		} catch ( \Exception $e ) {
			Helper::log( 'Failed to generate AI question: ' . $e->getMessage() );

			return null;
		}
	}

	/**
	 * Create the actual message object
	 *
	 * @param Parameter   $parameter              The parameter to include in the message
	 * @param string      $question               The question text
	 * @param array|null  $parameter_list_context Optional total_count, current_order, next_parameter, previous_parameter
	 *
	 * @return Message The constructed message
	 * @since 1.0.0
	 */
	private function create_message(
		Parameter $parameter,
		string $question,
		?array $parameter_list_context = null
	): Message {
		$action       = Action::find( $parameter->get_action_id() );
		$has_callback = $action && $action->has_callbacks();

		$parameter_content = [
			'type'                   => Message::CONTENT_TYPE_PARAMETER,
			'parameter'              => $parameter,
			'has_callback'           => $has_callback,
			'parameter_list_context' => $parameter_list_context
		];

		$text_content = [
			'type' => Message::CONTENT_TYPE_TEXT,
			'text' => [ 'value' => $question ],
		];

		$message = Message::make( [
			'role'    => Message::ROLE_ASSISTANT,
			'content' => [
				$text_content,
				$parameter_content,
			]
		] );

		$message->set_is_action_message( true );

		return $message;
	}

	private function get_ai_question_for_searchable_parameter( $search_results, Action_Plan $action_plan, Action_Plan_Step $step ) {
		$question = null;
		$first_step    = $action_plan->get_first_step();
		$is_first_step = $first_step === $step;
		$previous_step = $action_plan->get_previous_step();

		// Check if search was attempted (indicated by presence of search_query key)
		$search_attempted = ! empty( $search_results ) && isset( $search_results['search_query'] );

		if ( $is_first_step && ! $previous_step && $search_attempted && ! empty( $this->chatbot_utility ) ) {
			// This is the first step, no previous step, and a search was attempted
			$keyword     = $search_results['search_query'] ?? '';
			$has_results = ! empty( $search_results['results'] ) && is_array( $search_results['results'] ) && count( $search_results['results'] ) > 0;

			// Get the user's last message
			$last_user_message = $this->chatbot_utility->chat ? $this->chatbot_utility->chat->get_last_user_message() : null;
			$user_message_text = $last_user_message ? $last_user_message->extract_text() : '';

			// Generate AI-powered search result message
			try {
				$copilot    = new Copilot_Utility(
					$this->chatbot_utility->get_ai_model(),
					$this->chatbot_utility->get_config()
				);
				$ai_message = $copilot->generate_search_result_message( $user_message_text,
					$keyword,
					$has_results, $this->chatbot_utility->get_stream() );

				if ( $ai_message instanceof Message ) {
					$generated_text = $ai_message->extract_text();
					if ( ! empty( $generated_text ) ) {
						$question = $generated_text;
					}
				}
			} catch ( \Exception $e ) {
				Helper::log( 'Failed to generate AI search result message: ' . $e->getMessage() );
			}
		}
		return $question ?? null;
	}
}
