<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Widget_Collection;
use Limb_Chatbot\Includes\Widgets\Callbacks\Widget_Callback_Factory;
use Limb_Chatbot\Includes\Widgets\Items\Widget_Item;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

/**
 * Widgets Controller
 *
 * Handles widget callback requests for form submissions and interactions.
 *
 * @since 1.0.11
 */
class Widgets_Controller extends Rest_Controller {

	/**
	 * Route prefix.
	 *
	 * @var string
	 * @since 1.0.11
	 */
	protected $rest_base = 'widgets';

	/**
	 * Register widget routes.
	 *
	 * @since 1.0.11
	 */
	public function register_routes(): void {
		register_rest_route(
			$this->namespace,
			'/chatbots/(?P<chatbot_uuid>[a-zA-Z0-9_-]+)/' . $this->rest_base . '/(?P<widget_id>[a-zA-Z0-9_-]+)/callback',
			[
				'methods'             => 'POST',
				'callback'            => [ $this, 'handle_widget_callback' ],
				'permission_callback' => [ $this, 'check_public_permission' ],
				'args'                => $this->get_cpt_callback_args(),
			]
		);

		// Route for default chatbot
		register_rest_route(
			$this->namespace,
			'/chatbots/default/' . $this->rest_base . '/(?P<widget_id>[a-zA-Z0-9_-]+)/callback',
			[
				'methods'             => 'POST',
				'callback'            => [ $this, 'handle_widget_callback' ],
				'permission_callback' => [ $this, 'check_public_permission' ],
				'args'                => $this->get_default_args(),
			]
		);
	}

	private function get_cpt_callback_args() {
		return [
			'chatbot_uuid' => [
				'type'              => 'string',
				'required'          => true,
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => [ $this, 'validate_chatbot_uuid' ],
			],
			'widget_id'    => [
				'type'              => 'string',
				'required'          => true,
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => [ $this, 'validate_widget_id' ],
			],
			'params'       => [
				'type'              => 'object',
				'required'          => true,
				'sanitize_callback' => [ $this, 'sanitize_params' ],
				'validate_callback' => [ $this, 'validate_params' ],
			],
		];
	}

	private function get_default_args() {
		return [
			'widget_id' => [
				'type'              => 'string',
				'required'          => true,
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => [ $this, 'validate_widget_id' ],
			],
			'params'    => [
				'type'              => 'object',
				'required'          => true,
				'sanitize_callback' => [ $this, 'sanitize_params' ],
				'validate_callback' => [ $this, 'validate_params' ],
			],
		];
	}

	/**
	 * Validate chatbot UUID.
	 *
	 * @param  string  $value  UUID value to validate.
	 *
	 * @return bool|WP_Error True if valid, WP_Error otherwise.
	 * @since 1.0.11
	 */
	public function validate_chatbot_uuid( string $value ) {
		if ( empty( $value ) ) {
			return new WP_Error(
				'invalid_chatbot_uuid',
				__( 'Chatbot UUID cannot be empty', 'limb-chatbot' ),
				[ 'status' => 400 ]
			);
		}

		if ( ! preg_match( '/^[a-zA-Z0-9_-]+$/', $value ) ) {
			return new WP_Error(
				'invalid_chatbot_uuid_format',
				__( 'Chatbot UUID contains invalid characters', 'limb-chatbot' ),
				[ 'status' => 400 ]
			);
		}

		return true;
	}

	/**
	 * Validate widget ID.
	 *
	 * @param  string  $value  Widget ID to validate.
	 *
	 * @return bool|WP_Error True if valid, WP_Error otherwise.
	 * @since 1.0.11
	 */
	public function validate_widget_id( string $value ) {
		if ( empty( $value ) ) {
			return new WP_Error(
				'invalid_widget_id',
				__( 'Widget ID cannot be empty', 'limb-chatbot' ),
				[ 'status' => 400 ]
			);
		}

		if ( ! preg_match( '/^[a-zA-Z0-9_-]+$/', $value ) ) {
			return new WP_Error(
				'invalid_widget_id_format',
				__( 'Widget ID contains invalid characters', 'limb-chatbot' ),
				[ 'status' => 400 ]
			);
		}

		return true;
	}

	/**
	 * Sanitize params object.
	 *
	 * @param  array  $value  Params array.
	 *
	 * @return array Sanitized value.
	 * @since 1.0.11
	 */
	public function sanitize_params( $value ): array {
		if ( ! is_array( $value ) ) {
			return [];
		}

		$sanitized = [];

		foreach ( $value as $key => $val ) {
			// Sanitize key
			$clean_key = sanitize_key( $key );

			// Sanitize value based on type
			if ( is_string( $val ) ) {
				$sanitized[ $clean_key ] = sanitize_text_field( $val );
			} elseif ( is_array( $val ) ) {
				$sanitized[ $clean_key ] = wp_kses_post_deep( $val );
			} elseif ( is_bool( $val ) || is_int( $val ) || is_float( $val ) ) {
				$sanitized[ $clean_key ] = $val;
			} else {
				$sanitized[ $clean_key ] = '';
			}
		}

		return $sanitized;
	}

	/**
	 * Validate params object.
	 *
	 * @param  mixed  $value  Params to validate.
	 *
	 * @return bool|WP_Error True if valid, WP_Error otherwise.
	 * @since 1.0.11
	 */
	public function validate_params( $value ) {
		if ( ! is_array( $value ) && ! is_object( $value ) ) {
			return new WP_Error(
				'invalid_params_type',
				__( 'Params must be an object or array', 'limb-chatbot' ),
				[ 'status' => 400 ]
			);
		}

		return true;
	}

	/**
	 * Handle widget callback request.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_Error|WP_REST_Response
	 * @since 1.0.11
	 */
	public function handle_widget_callback( WP_REST_Request $request ) {
		try {
			$chatbot_uuid = $request->get_param( 'chatbot_uuid' ) ?? 'default';
			$widget_id    = $request->get_param( 'widget_id' );
			$params       = $request->get_param( 'params' );
			$chatbot      = $this->get_chatbot( $chatbot_uuid );
			$widget_item  = $this->get_widget_item( $chatbot, $widget_id );
			$widget_type  = $widget_item->type ?? null;
			$factory      = new Widget_Callback_Factory();
			$callback     = $factory->make( $widget_type );
			$result       = $callback->execute( $widget_item, $params, $chatbot );

			return rest_ensure_response( $result );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Get chatbot by UUID or return default.
	 *
	 * @param  string  $chatbot_uuid  Chatbot UUID or 'default'.
	 *
	 * @return Chatbot|null Chatbot instance or null.
	 * @throws Exception
	 * @since 1.0.11
	 */
	private function get_chatbot( string $chatbot_uuid ): ?Chatbot {
		if ( $chatbot_uuid === 'default' ) {
			$chatbot = Chatbot::make();
		} else {
			$chatbot = Chatbot::find_by_uuid( $chatbot_uuid );
		}

		if ( ! $chatbot instanceof Chatbot ) {
			throw new Exception( Error_Codes::NOT_FOUND, __( 'Chatbot not found', 'limb-chatbot' ) );
		}

		return $chatbot;
	}

	/**
	 * Get widget item by ID from chatbot.
	 *
	 * @param  Chatbot  $chatbot  Chatbot instance.
	 * @param  string  $widget_id  Widget ID to find.
	 *
	 * @return Widget_Item Widget item or null.
	 * @throws Exception
	 * @since 1.0.11
	 */
	private function get_widget_item( Chatbot $chatbot, string $widget_id ) {
		$widgets = $chatbot->get_parameter( 'widgets' );
		if ( ! $widgets instanceof Widget_Collection ) {
			throw new Exception( Error_Codes::NOT_FOUND, __( 'Widget not found', 'limb-chatbot' ) );
		}
		if ( $widgets->is_empty() ) {
			throw new Exception( Error_Codes::NOT_FOUND, __( 'Widget not found', 'limb-chatbot' ) );
		}
		foreach ( $widgets->get() as $widget ) {
			if ( ! $widget instanceof Widget_Item ) {
				continue;
			}
			if ( isset( $widget->id ) && $widget->id === $widget_id ) {
				return $widget;
			}
		}
		throw new Exception( Error_Codes::NOT_FOUND, __( 'Widget not found', 'limb-chatbot' ) );
	}

	/**
	 * Check public permission (allow all).
	 *
	 * @return bool Always true for widget callbacks.
	 * @since 1.0.11
	 */
	public function check_public_permission(): bool {
		return true;
	}
}
