<?php

namespace Limb_Chatbot\Includes\Services\Actions;

use Limb_Chatbot\Includes\Data_Objects\Action_Plan;
use Limb_Chatbot\Includes\Data_Objects\Message;
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 Action_Cancellation_Message_Builder
 *
 * Builds AI-powered cancellation messages when users cancel an action/form.
 * Uses Copilot to generate warm, context-aware messages with a static fallback.
 *
 * @package Limb_Chatbot\Includes\Services\Actions
 * @since 1.0.0
 */
class Action_Cancellation_Message_Builder {

	private const FALLBACK_MESSAGE = 'No problem! Form cancelled. How else can I help you?';

	/**
	 * @var Chatbot_Utility|null
	 */
	private ?Chatbot_Utility $chatbot_utility;

	/**
	 * @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 cancellation acknowledgment message.
	 *
	 * Attempts AI generation via Copilot, falls back to a static message
	 * if AI is unavailable or fails. When streaming is enabled, AI-generated text
	 * is streamed by the AI endpoint; fallback text is streamed manually.
	 *
	 * @param  Action_Plan  $action_plan  The cancelled action plan (for context)
	 * @param  bool  $stream  Whether streaming is enabled
	 *
	 * @return Message The cancellation message
	 * @since 1.0.0
	 */
	public function build( Action_Plan $action_plan, bool $stream = false ): Message {
		$text = $this->generate_text( $action_plan, $stream );

		return Message::make( [
			'role'    => Message::ROLE_ASSISTANT,
			'content' => [
				[
					'type' => Message::CONTENT_TYPE_TEXT,
					'text' => [ 'value' => $text ],
				],
			],
		] );
	}

	/**
	 * Generate the cancellation message text with AI or fallback.
	 *
	 * When AI succeeds with streaming, the AI endpoint handles streaming internally.
	 * When falling back, streaming is handled manually here.
	 *
	 * @param  Action_Plan  $action_plan  The cancelled action plan
	 * @param  bool  $stream  Whether streaming is enabled
	 *
	 * @return string The message text
	 */
	private function generate_text( Action_Plan $action_plan, bool $stream ): string {
		if ( empty( $this->chatbot_utility ) ) {
			return $this->stream_fallback( $stream );
		}

		try {
			$copilot = new Copilot_Utility(
				$this->chatbot_utility->get_ai_model(),
				$this->chatbot_utility->get_config()
			);

			$action_context = $this->get_action_context( $action_plan );
			$ai_message     = $copilot->generate_action_cancellation_message( $action_context, $stream );

			if ( $ai_message instanceof Message ) {
				$text = $ai_message->extract_text();
				if ( ! empty( trim( $text ) ) ) {
					return $text;
				}
			}

			return $this->stream_fallback( $stream );
		} catch ( \Exception $e ) {
			Helper::log( 'Failed to generate AI cancellation message: ' . $e->getMessage() );

			return $this->stream_fallback( $stream );
		}
	}

	/**
	 * Return the fallback message and stream it if streaming is enabled.
	 *
	 * @param  bool  $stream  Whether streaming is enabled
	 *
	 * @return string The fallback message text
	 */
	private function stream_fallback( bool $stream ): string {
		$text = $this->get_fallback_message();

		if ( $stream ) {
			Stream_Event_Service::text( $text );
		}

		return $text;
	}

	/**
	 * Get a user-friendly action context string from the action plan.
	 *
	 * @param  Action_Plan  $action_plan  The action plan
	 *
	 * @return string Action context (e.g., "Contact Form")
	 */
	private function get_action_context( Action_Plan $action_plan ): string {
		$action = $action_plan->action();
		if ( ! $action ) {
			return '';
		}

		$name = $action->get_name() ?? '';
		$name = str_replace( [ '_', '-' ], ' ', $name );

		return ucwords( $name );
	}

	/**
	 * @return string Static fallback message
	 */
	private function get_fallback_message(): string {
		return __( self::FALLBACK_MESSAGE, 'limb-chatbot' );
	}
}
