<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Limb_Chatbot\Includes\Data_Schemas\Setting as Setting_Schema;
use Limb_Chatbot\Includes\Services\Chatbot_Payload;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Preview_State;
use Limb_Chatbot\Includes\Repositories\Setting_Repository;
use Limb_Chatbot\Includes\Services\Sanitizer;
use Limb_Chatbot\Includes\Services\Validator;
use Limb_Chatbot\Includes\Api\V1\Controllers\Widgets_Controller;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

/**
 * Controller for managing chatbot preview state (settings + widgets).
 *
 * This controller exposes a single REST base that:
 * - Updates preview parameters/widgets.
 * - Clears preview state.
 * - Always returns Chatbot_Payload based on the current preview state.
 *
 * @since 1.0.0
 */
class Preview_State_Controller extends Rest_Controller {

	/**
	 * REST route base.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected $rest_base = 'chatbot/preview-state';

	protected Sanitizer $sanitize;
	protected Validator $validator;
	protected Chatbot_Payload $chatbot_payload;

	public function __construct() {
		$schema             = new Setting_Schema();
		$this->sanitize     = new Sanitizer( $schema );
		$this->validator    = new Validator( $schema );
		$this->chatbot_payload = new Chatbot_Payload();
	}

	/**
	 * Registers REST routes.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register_routes() {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			array(
				array(
					'methods'             => \WP_REST_Server::EDITABLE,
					'callback'            => array( $this, 'update_state' ),
					'permission_callback' => array( $this, 'admin_permission_callback' ),
				),
				array(
					'methods'             => \WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'delete_state' ),
					'permission_callback' => array( $this, 'admin_permission_callback' ),
				),
				'schema' => array( $this, 'get_item_schema' ),
			)
		);
	}

	/**
	 * Ensure only admins can manage preview state.
	 *
	 * @param  WP_REST_Request  $request
	 *
	 * @return bool
	 */
	public function admin_permission_callback( $request ): bool {
		if ( ! parent::permission_callback( $request ) ) {
			return false;
		}

		return current_user_can( 'manage_options' );
	}

	/**
	 * Update preview parameters/widgets and return Chatbot_Payload based on preview state.
	 *
	 * Expects JSON body: [ { "key": string, "value": mixed }, ... ]
	 *
	 * @param  WP_REST_Request  $request
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function update_state( WP_REST_Request $request ) {
		try {
			$widgets_key = 'lbaic.utilities.chatbot.widgets';

			$items = $request->get_json_params();
			if ( ! is_array( $items ) ) {
				return rest_ensure_response(
					new WP_Error(
						'invalid_body',
						__( 'Request body must be an array of { key, value } items.', 'limb-chatbot' ),
						array( 'status' => 400 )
					)
				);
			}

			$assoc_items = array_column( $items, 'value', 'key' );
			$preview_state = Preview_State::load();

			$parameters = $preview_state->get_parameters();
			$widgets    = $preview_state->get_widgets();

			// Widgets must be sanitized/validated with widget controller logic.
			$widgets_overridden = array_key_exists( $widgets_key, $assoc_items );
			if ( $widgets_overridden ) {
				$raw_widgets_value = $assoc_items[ $widgets_key ];

				if ( is_array( $raw_widgets_value ) && empty( $raw_widgets_value ) ) {
					// Allow explicit "clear" of preview widgets.
					$widgets = [];
				} else {
					$widgets_controller = new Widgets_Controller();
					$sanitized_widgets  = $widgets_controller->sanitize_batch_items( $raw_widgets_value );
					$widgets = $sanitized_widgets;
				}
			}

			// Validate non-widget keys with settings schema (no `.preview` postfix needed).
			$non_widget_items = $assoc_items;
			unset( $non_widget_items[ $widgets_key ] );
			$param_keys = array_keys( $non_widget_items );
			$validated_params = [];

			if ( ! empty( $param_keys ) ) {
				$this->sanitize->sanitize( $non_widget_items, 'create', false, $param_keys );
				$this->validator->validate( $this->sanitize->get_sanitized(), 'create', false, $param_keys );

				if ( $this->validator->has_errors() ) {
					return rest_ensure_response(
						new WP_Error(
							'validation_error',
							__( 'Validation errors occurred.', 'limb-chatbot' ),
							array(
								'status' => 400,
								'errors' => $this->validator->get_error_array(),
							)
						)
					);
				}

				$validated_params = $this->validator->get_validated();
			}

			foreach ( $validated_params as $key => $value ) {
				$parameters[ $key ] = $value;
			}

			$preview_state = $preview_state->with_merged_parameters( $parameters );
			if ( $widgets_overridden ) {
				$preview_state = $preview_state
					->with_widgets( $widgets )
					->with_widgets_override( true );
			}

			$preview_state->save();

			// Return payload based on updated preview state.
			$payload = $this->chatbot_payload->prepare( true );

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

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

	/**
	 * Delete preview state and return Chatbot_Payload based on non-preview settings.
	 *
	 * @param  WP_REST_Request  $request
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function delete_state( WP_REST_Request $request ) {
		try {
			Preview_State::clear();
			// Also clear legacy `.preview` settings to avoid stale preview values leaking back in.
			( new Setting_Repository() )->remove_preview_settings();

			// After clearing, payload will be based on saved settings only.
			$payload = $this->chatbot_payload->prepare( false );

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

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

