<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Config;
use Limb_Chatbot\Includes\Data_Objects\Dataset;
use Limb_Chatbot\Includes\Data_Objects\AI_Model;
use Limb_Chatbot\Includes\AI_Providers\AI_Provider;
use Limb_Chatbot\Includes\AI_Providers\AI_Providers;
use Limb_Chatbot\Includes\Data_Objects\Dataset_Meta;
use Limb_Chatbot\Includes\Repositories\AI_Model_Repository;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\AI_Model_Service;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * REST API Controller for AI Models.
 *
 * Provides endpoints to retrieve, create, delete, and fine-tune AI models.
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.0
 */
class AI_Models_Controller extends Rest_Controller {

	/**
	 * REST base route for AI models.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected $rest_base = 'ai_models';

	/**
	 * Repository for AI Model data operations.
	 *
	 * @var AI_Model_Repository
	 * @since 1.0.0
	 */
	protected AI_Model_Repository $repository;

	/**
	 * Service layer for AI Model business logic.
	 *
	 * @var AI_Model_Service
	 * @since 1.0.0
	 */
	protected AI_Model_Service $service;

	/**
	 * Constructor.
	 *
	 * Initializes AI Model service.
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->service = new AI_Model_Service();
	}

	/**
	 * Registers REST API routes for the controller.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register_routes() {
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			'methods'             => WP_REST_Server::READABLE,
			'callback'            => array( $this, 'get_items' ),
			'permission_callback' => array( $this, 'permission_callback' ),
			'args'                => $this->get_collection_params()
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array(
					'id' => array(
						'type'              => 'int',
						'validate_callback' => array( $this, 'validate_ai_model_id' ),
					)
				),
			),
			array(
				'methods'             => WP_REST_Server::DELETABLE,
				'callback'            => array( $this, 'delete_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array( 'id' => $this->get_tuning_params()['ai_model_id'] ),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'create_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_endpoint_args_for_item_schema(),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
//		register_rest_route( $this->namespace, '/' . $this->rest_base . '/tuning', array(
//			array(
//				'methods'             => WP_REST_Server::CREATABLE,
//				'callback'            => array( $this, 'tuning' ),
//				'args'                => $this->get_tuning_params(),
//				'permission_callback' => array( $this, 'permission_callback' ),
//			),
//		) );
	}

	/**
	 * Returns parameters for collection queries.
	 *
	 * Extends the parent params with orderby, ai_provider_id, and include.
	 *
	 * @return array Collection query parameters.
	 * @since 1.0.0
	 */
	public function get_collection_params() {
		return array_merge( parent::get_collection_params(), array(
			'orderby'        => array(
				'description'       => __( 'Sort the collection by attribute.', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => 'id',
				'enum'              => array( 'id', 'ai_provider_id', 'name' ),
				'validate_callback' => 'rest_validate_request_arg',
			),
			'ai_provider_id' => array(
				'required'          => false,
				'validate_callback' => function ( $ai_provider_id ) {
					return ! empty( AI_Providers::instance()->get_ai_provider( $ai_provider_id ) );
				}
			),
			'include'        => array(
				'type' => 'array',
			),
			'name'           => array(
				'description'       => __( 'The name of the model id', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function ( $value ) {
					return ! empty( $value ) && is_string( $value );
				}
			)
		) );
	}

	/**
	 * Returns tuning parameters for fine-tuning models.
	 *
	 * Includes validation callbacks to ensure data consistency.
	 *
	 * @return array Tuning parameters.
	 * @since 1.0.0
	 */
	private function get_tuning_params(): array {
		return array(
			'suffix'      => array(
				'type'              => 'string',
				'required'          => true,
				'validate_callback' => function ( $value ) {
					return ! empty( $value );
				}
			),
			'ai_model_id' => array(
				'type'              => 'int',
				'required'          => true,
				'validate_callback' => function ( $value ) {
					if ( ! ( $ai_model = AI_Model::find( $value ) ) ) {
						return false;
					}

					return $ai_model->has_feature( 'fine_tuning' );
				}
			),
			'config_id'   => array(
				'type'              => 'int',
				'required'          => true,
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function ( $value, $request ) {
					return (bool) Config::count( [ 'id' => $value ] );
				}
			),
			'dataset_id'  => array(
				'type'              => 'int',
				'required'          => true,
				'validate_callback' => function ( $value, $request ) {
					if ( ! ( $dataset = Dataset::find( $value ) ) ) {
						return new WP_Error( 'not_found', __( 'Dataset not found.', 'limb-chatbot' ) );
					}
					$ai_provider = AI_Providers::instance()->get_ai_provider( AI_Model::find( $request->get_param( 'ai_model_id' ) )->get_ai_provider_id() );
					if ( ! $ai_provider instanceof AI_Provider ) {
						return new WP_Error( 'not_found', __( 'Ai Provider not found.', 'limb-chatbot' ) );
					}
					if ( $dataset->get_entries_count() < $ai_provider::FINE_TUNING_MIN_ENTRY_COUNT ) {
						// translators: %s is the name of the AI provider, %d is the minimum required number of entries for fine-tuning.
						return new WP_Error( 'min_entries_count', sprintf( __( 'To fine-tune model in %1$s you should have at least %2$d entries', 'limb-chatbot' ), $ai_provider::$name, $ai_provider::FINE_TUNING_MIN_ENTRY_COUNT ) );
					}

					return true;
				}
			),
			'variables'   => array(
				'type'              => 'object',
				'required'          => false,
				'validate_callback' => function ( $value, $request ) {
					$meta = Dataset_Meta::where( [ $request->get_param( 'dataset_id' ), 'meta_key' => 'variables' ] )->first();
					if ( ! ( $variables = $meta->get_meta_value() ) ) {
						return new WP_Error( 'invalid_param', __( 'There are not variables for this dataset.', 'limb-chatbot' ) );
					}
					if ( $diff = array_diff( array_keys( $value ), array_keys( $variables ) ) ) {
						// translators: %s is a comma-separated list of invalid variable names.
						return new WP_Error( 'invalid_param', sprintf( __( 'Invalid variables %s.', 'limb-chatbot' ), implode( ', ', array_values( $diff ) ) ) );
					}

					return true;
				}
			)
		);
	}

	/**
	 * Returns the JSON schema for AI Model resources.
	 *
	 * Used for validation and API documentation.
	 *
	 * @return array JSON schema for AI Model.
	 * @since 1.0.0
	 */
	public function get_item_schema() {
		if ( $this->schema ) {
			return $this->schema;
		}
		$this->schema = [
			'$schema'    => 'http://json-schema.org/draft-07/schema#',
			'title'      => 'model',
			'type'       => 'object',
			'properties' => [
				'id'                 => [
					'description' => __( 'Unique identifier for the model.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => [ 'view', 'edit' ],
					'readonly'    => true,
					'arg_options' => array(
						'validate_callback' => array( $this, 'validate_ai_model_id' )
					)
				],
				'name'               => [
					'description' => __( 'Name of the AI model.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => [ 'view', 'edit' ],
					'required'    => true,
					'arg_options' => array(
						'required'          => true,
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => function ( $value ) {
							return empty( AI_Model::find_by_name( $value ) );
						},
					)
				],
				'ai_provider_id'     => [
					'description' => __( 'Ai Provider identifier.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => [ 'view', 'edit' ],
					'required'    => true,
					'arg_options' => array(
						'required'          => true,
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => function ( $value ) {
							return ! empty( AI_Providers::instance()->get_ai_provider( $value ) );
						}
					)
				],
				'capabilities'       => [
					'description' => __( 'Capabilities of the model.', 'limb-chatbot' ),
					'type'        => [ 'array', 'object', 'null' ],
					'context'     => [ 'view', 'edit' ],
				],
				'fine_tuned'         => [
					'description' => __( 'Whether the model is fine-tuned.', 'limb-chatbot' ),
					'type'        => 'boolean',
					'context'     => [ 'view', 'edit' ],
				],
				'default'            => [
					'description' => __( 'Whether the model is set as default.', 'limb-chatbot' ),
					'type'        => 'boolean',
					'context'     => [ 'view', 'edit' ],
				],
				'output_token_limit' => [
					'description' => __( 'Maximum output token limit.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => [ 'view', 'edit' ],
				],
				'description'        => [
					'description' => __( 'AI_Model description.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => [ 'view', 'edit' ],
				],
				'status'             => [
					'description' => __( 'AI_Model status (1 = active, 2 = inactive).', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => [ 'view', 'edit' ],
				],
			],
		];

		return $this->schema;
	}

	/**
	 * Handles fine-tuning request for AI Models.
	 *
	 * @param  WP_REST_Request  $request  REST request.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.0
	 */
	public function permission_callback( $request ): bool {
		if ( ! parent::permission_callback( $request ) ) {
			return false;
		}

		return current_user_can( 'manage_options' );
	}

//	/**
//	 * Retrieves a collection of AI Models.
//	 *
//	 * @param  WP_REST_Request  $request  REST request.
//	 *
//	 * @return WP_REST_Response|WP_Error Response or error.
//	 * @since 1.0.0
//	 */
//	public function tuning( $request ) {
//		try {
//			return rest_ensure_response( $this->service->tuning( $request->get_params() ) );
//		} catch ( Exception $e ) {
//			Helper::log( $e, __METHOD__ );
//
//			return Helper::get_wp_error( $e );
//		}
//	}

	/**
	 * Retrieves a single AI Model by ID.
	 *
	 * @param  WP_REST_Request  $request  REST request.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.0
	 */
	public function get_items( $request ) {
		try {
			return rest_ensure_response( $this->prepare_collection( $this->service->get_items( $request->get_query_params() ), $request ) );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Retrieves a single AI Model by ID.
	 *
	 * @param  WP_REST_Request  $request  REST request.
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.0
	 */
	public function get_item( $request ) {
		try {
			$model = AI_Model::find( $request->get_param( 'id' ) );

			if ( ! $model ) {
				return new WP_Error( 'not_found', __( 'AI Model not found.', 'limb-chatbot' ), [ 'status' => 404 ] );
			}

			// Handle include parameter for relationships
			$include = $request->get_param( 'include' );
			if ( ! empty( $include ) && is_array( $include ) ) {
				foreach ( $include as $relation ) {
					if ( method_exists( $model, $relation ) ) {
						$args = $relation === 'metas' && ! empty( $request->get_param( 'meta_keys' ) )
							? [ 'meta_key' => $request->get_param( 'meta_keys' ) ]
							: [];
						$model->included[ $relation ] = $model->{$relation}( $args );
					}
				}
			}

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

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

	/**
	 * Deletes a single AI Model by ID.
	 *
	 * @param  WP_REST_Request  $request  REST request.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.0
	 */
	public function delete_item( $request ) {
		try {
			return rest_ensure_response( $this->service->delete( $request->get_param( 'id' ) ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Validates if the AI Model ID exists.
	 *
	 * @param  mixed  $value  The AI Model ID to validate.
	 *
	 * @return bool True if model exists; false otherwise.
	 * @since 1.0.0
	 */
	public function validate_ai_model_id( $value ): bool {
		return (bool) AI_Model::count( [ 'id' => $value ] );
	}
}