<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Dataset;
use Limb_Chatbot\Includes\Data_Objects\Setting;
use Limb_Chatbot\Includes\Data_Schemas;
use Limb_Chatbot\Includes\Repositories\Dataset_Repository;
use Limb_Chatbot\Includes\Services\Dataset_Service;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Validator;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * REST API controller for managing datasets.
 *
 * Provides CRUD operations, upload, and import functionality.
 *
 * @since 1.0.0
 */
class Datasets_Controller extends Rest_Controller {


	/**
	 * Base REST route for datasets.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected $rest_base = 'datasets';

	/**
	 * Dataset service instance.
	 *
	 * @var Dataset_Service
	 * @since 1.0.0
	 */
	protected Dataset_Service $service;

	/**
	 * Dataset repository instance.
	 *
	 * @var Dataset_Repository
	 * @since 1.0.0
	 */
	protected Dataset_Repository $repository;

	/**
	 * Dataset CSV file schema validator.
	 *
	 * @var Data_Schemas\Dataset_Csv_File
	 * @since 1.0.0
	 */
	protected Data_Schemas\Dataset_Csv_File $csv_file_schema;

	/**
	 * Constructor.
	 *
	 * Initializes repository, service, and CSV schema instances.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->repository      = new Dataset_Repository();
		$this->csv_file_schema = new Data_Schemas\Dataset_Csv_File();
		$this->service         = new Dataset_Service( $this->repository );
	}


	/**
	 * Registers REST API routes.
	 *
	 * @since 1.0.0
	 */
	public function register_routes(): void {
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'permission_callback' => array( $this, 'permission_callback' ),
				'callback'            => array( $this, 'get_items' ),
			),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'create_item' ),
				'args'                => $this->get_endpoint_args_for_item_schema(),
				'permission_callback' => array( $this, 'permission_callback' ),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
		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' ),
			),
			array(
				'methods'             => WP_REST_Server::DELETABLE,
				'callback'            => array( $this, 'delete_item' ),
				'args'                => array_merge( $this->validate_dataset_id() ),
				'permission_callback' => array( $this, 'permission_callback' ),
			),
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'update_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 . '/upload', array(
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'upload' ),
				'permission_callback' => array( $this, 'permission_callback' ),
			),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/regenerate', array(
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'regenerate_from_content' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array_merge(
					$this->validate_dataset_id(),
					array(
						'source_content' => array(
							'type'              => 'string',
							'required'          => true,
							'validate_callback' => function ( $value ) {
								return ! empty( trim( $value ) );
							},
						),
						'title' => array(
							'type'              => 'string',
							'required'          => false,
							'validate_callback' => function ( $value ) {
								return ! empty( trim( $value ) );
							},
						),
					)
				),
			),
		) );
//		register_rest_route( $this->namespace, '/' . $this->rest_base . '/import', array(
//			array(
//				'methods'             => WP_REST_Server::CREATABLE,
//				'callback'            => array( $this, 'import' ),
//				'permission_callback' => array( $this, 'permission_callback' ),
//				'args'                => array(
//					'uuid' => array(
//						'type'              => 'string',
//						'format'            => 'uuid',
//						'required'          => true,
//						'validate_callback' => function ( $value, $request ) {
//							$existing_value = Setting::find( Setting::SETTING_PREFIX . 'files.datasets' )->get_value();
//							$uuids          = is_array( $existing_value ) ? array_column( $existing_value, 'uuid' ) : null;
//							if ( is_null( $uuids ) || ! in_array( $value, $uuids, true ) ) {
//								return false;
//							}
//
//							return true;
//						},
//					)
//				)
//			),
//		) );
	}

	/**
	 * Validates the dataset ID parameter.
	 *
	 * @return array Argument definition for dataset ID.
	 * @since 1.0.0
	 */
	private function validate_dataset_id(): array {
		return array(
			'id' => array(
				'type'              => 'int',
				'validate_callback' => function ( $value, $request, $param ) {
					return ! empty( Dataset::find( $value ) );
				}
			)
		);
	}

	/**
	 * Permission callback to check user capabilities.
	 *
	 * Allows only users with 'manage_options' capability.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return bool True if user has permission, false otherwise.
	 * @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 datasets.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function get_items( $request ) {
		try {
			$collection = $this->repository->get_items( $request->get_query_params() );
			$collection = $this->prepare_collection( $collection, $request );

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

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

	/**
	 * Prepares dataset collection, adding relevant links.
	 *
	 * @param mixed $collection Collection of dataset items.
	 * @param WP_REST_Request $request Incoming REST request.
	 * @return mixed Prepared collection with links.
	 * @since 1.0.0
	 */
	public function prepare_collection( $collection, $request ) {
		if ( $collection->is_empty() ) {
			return $collection;
		}
		if ( $include = $request->get_param( 'include' ) ) {
			$include    = array_filter( $include, function ( $property ) use ( $collection ) {
				return ! property_exists( $collection->first(), $property );
			} );
			$collection = $collection->with( $include );
		}

		return $this->set_pagination_params( $collection, $request );
	}

	/**
	 * Prepares HAL-style links for dataset entries.
	 *
	 * @param  Dataset|array  $objects  Single dataset or array of datasets.
	 *
	 * @return array Prepared dataset(s) with entry links.
	 * @since 1.0.0
	 */
	private function prepare_entry_links( $objects ) {
		$single = false;
		if ( $objects instanceof Dataset ) {
			$objects = array( $objects );
			$single  = true;
		}
		$preparedMappings = array();
		foreach ( $objects as $object ) {
			$links              = array(
				'entries' => array(
					'href' => rest_url( sprintf( '/%s/%s/%d/%s', $this->namespace, $this->rest_base, $object->id, Dataset_Entries_Controller::REST_ENDPOINT ) ),
				),
			);
			$preparedMappings[] = array(
				'dataset' => $object,
				'_links'  => $links,
			);
		}

		return $single ? $preparedMappings[0] : $preparedMappings;
	}

	/**
	 * Creates a new dataset.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function create_item( $request ) {
		try {
			return rest_ensure_response( $this->prepare_entry_links( Dataset::create( $request->get_json_params() ) ) );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Handles CSV file upload for datasets.
	 *
	 * Validates file and processes upload.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function upload( $request ) {
		try {
			$validator = ( new Validator( $this->csv_file_schema ) );
			$validator->validate( [ 'file' => $request->get_file_params( 'file' ) ] );
			if ( $validator->has_errors() ) {
				return rest_ensure_response( $validator->get_error() );
			}

			return rest_ensure_response( $this->service->upload( $validator->get_validated()['file'] ) );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Retrieves a single dataset by ID.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function get_item( $request ) {
		try {
			$response = $this->repository->get_item( $request->get_params());
			return rest_ensure_response( $this->prepare_entry_links( $response ));
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

//	/**
//	 * Imports dataset from uploaded CSV using UUID.
//	 *
//	 * @param  WP_REST_Request  $request  Incoming REST request.
//	 *
//	 * @return WP_REST_Response|WP_Error REST response or error.
//	 * @since 1.0.0
//	 */
//	public function import( $request ) {
//		try {
//			return rest_ensure_response( $this->service->import( $request->get_json_params()['uuid'] ) );
//		} catch ( Exception $e ) {
//			Helper::log( $e, __METHOD__ );
//
//			return Helper::get_wp_error( $e );
//		}
//	}

	/**
	 * Deletes a dataset by ID.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 * @return WP_REST_Response|WP_Error REST 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 );
		}
	}

	/**
	 * Updates a dataset by ID.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function update_item( $request ) {
		try {
			$id      = $request->get_param( 'id' );
			$params  = $request->get_json_params();
			$updated = Dataset::update( [ 'id' => $id ], $params );
			$updated = $this->prepare_entry_links( $updated );

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

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

	/**
	 * Regenerates dataset entries from source_content meta.
	 *
	 * Delegates to Dataset_Service for business logic.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function regenerate_from_content( $request ) {
		try {
			$dataset_id = (int) $request->get_param( 'id' );
			$dataset = Dataset::find($dataset_id);
			$source_content = $request->get_json_params()['source_content'] ?? '';
			$title = $request->get_json_params()['title'] ?? $dataset->get_name();

			// Delegate to service
			$result = $this->service->regenerate_from_content( $dataset_id, $title, $source_content );

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

	/**
	 * Returns JSON schema for dataset resource.
	 *
	 * Defines structure and validation rules for dataset.
	 *
	 * @return array JSON schema array.
	 * @since 1.0.0
	 */
	public function get_item_schema(): array {
		if ( $this->schema ) {
			// Return cached schema if already set.
			return $this->schema;
		}
		$this->schema = array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'custom_item',
			'type'       => 'object',
			'properties' => array(
				'id'         => array(
					'description' => esc_html__( 'Unique identifier for the object.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit', 'embed' ),
					'readonly'    => true,
				),
				'name'       => array(
					'description' => __( 'The name of the item.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'required'    => false,
					'arg_options' => array(
						'validate_callback' => function ( $value ) {
							return ! empty( $value ) && is_string( $value ) && strlen( $value ) <= 255;
						}
					),
				),
				'created_at' => array(
					'description' => __( 'The timestamp when the item was created.', 'limb-chatbot' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'context'     => array( 'view' ),
					'readonly'    => true,
				),
				'updated_at' => array(
					'description' => __( 'The timestamp when the item was last updated.', 'limb-chatbot' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'type'       => array(
					'description' => __( 'The type of the dataset.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'required'    => false,
					'arg_options' => array(
						'validate_callback' => function ( $value ) {
							return ! empty( $value ) && in_array( $value,
									[ Dataset::TYPE_INFORMATIONAL_KNOWLEDGE, Dataset::TYPE_TRAINING ] );
						}
					)
				)
			),
		);

		return $this->schema;
	}
}