<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Ai_Providers\AI_Providers;
use Limb_Chatbot\Includes\Data_Objects\Dataset;
use Limb_Chatbot\Includes\Data_Objects\File;
use Limb_Chatbot\Includes\Repositories\File_Repository;
use Limb_Chatbot\Includes\Services\File_Service;
use Limb_Chatbot\Includes\Services\File_Upload_Service;
use Limb_Chatbot\Includes\Services\Helper;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * REST API controller for managing files.
 *
 * Provides endpoints to retrieve files uploaded to AI providers.
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.0
 */
class Files_Controller extends Rest_Controller {

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

	/**
	 * File repository instance.
	 *
	 * @var File_Repository
	 * @since 1.0.0
	 */
	protected File_Repository $repository;

	/**
	 * Constructor.
	 *
	 * Initializes the File repository instance.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->repository   = new File_Repository();
		$this->file_service = new File_Service();
	}

	/**
	 * Registers REST API routes for files.
	 *
	 * @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' ),
				'args'                => $this->get_collection_params(),
			),
		) );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<uuid>[a-f0-9\-]{36})', array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'permission_callback' => array( $this, 'permission_callback' ),
				'callback'            => array( $this, 'get_item' ),
				'args'                => $this->validate_file_uuid(),
			),
			array(
				'methods'             => WP_REST_Server::DELETABLE,
				'permission_callback' => array( $this, 'permission_callback' ),
				'callback'            => array( $this, 'delete_item' ),
				'args'                => $this->validate_file_uuid(),
			),
		) );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/refresh', array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'permission_callback' => array( $this, 'permission_callback' ),
				'callback'            => array( $this, 'refresh' ),
				'args'                => $this->validate_refresh_params(),
			),
		) );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/upload/batch', array(
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'batch_upload' ),
				'permission_callback' => array( $this, 'permission_callback' ),
			)
		) );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/import/media', array(
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'import_from_media' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_import_media_params(),
			)
		) );
	}

	/**
	 * Retrieves the query params for collections.
	 *
	 * @return array Query parameters.
	 * @since 1.0.0
	 */
	public function get_collection_params(): array {
		return array(
			'page'              => array(
				'description'       => __( 'Current page of the collection.', 'limb-chatbot' ),
				'type'              => 'integer',
				'default'           => 1,
				'sanitize_callback' => 'absint',
				'validate_callback' => function ( $value ) {
					return $value >= 1;
				},
			),
			'per_page'          => array(
				'description'       => __( 'Maximum number of items to be returned in result set.', 'limb-chatbot' ),
				'type'              => 'integer',
				'default'           => 10,
				'minimum'           => 1,
				'maximum'           => 100,
				'sanitize_callback' => 'absint',
			),
			'search'            => array(
				'description'       => __( 'Limit results to those matching a string.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'orderby'           => array(
				'description' => __( 'Sort collection by object attribute.', 'limb-chatbot' ),
				'type'        => 'string',
				'default'     => 'created_at',
				'enum'        => array( 'created_at', 'file_name', 'original_name', 'file_size', 'status' ),
			),
			'order'             => array(
				'description' => __( 'Order sort attribute ascending or descending.', 'limb-chatbot' ),
				'type'        => 'string',
				'default'     => 'desc',
				'enum'        => array( 'asc', 'desc' ),
			),
			'status'            => array(
				'description'       => __( 'Filter files by status.', 'limb-chatbot' ),
				'type'              => 'string',
				'enum'              => array( 'uploaded', 'failed' ),
				'sanitize_callback' => 'sanitize_text_field',
			),
			'external_provider' => array(
				'description'       => __( 'Filter files by external AI provider.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function ( $value, $request, $param ) {
					return in_array( $value, array_keys( AI_Providers::instance()->get_ai_providers() ) );
				},
			),
			'uuids'             => array(
				'description'       => __( 'Filter files by specific UUIDs.', 'limb-chatbot' ),
				'type'              => 'array',
				'items'             => array(
					'type'   => 'string',
					'format' => 'uuid',
				),
				'sanitize_callback' => function ( $value ) {
					if ( ! is_array( $value ) ) {
						return array();
					}

					return array_map( 'sanitize_text_field', $value );
				},
				'validate_callback' => function ( $value, $request, $param ) {
					if ( ! is_array( $value ) ) {
						return false;
					}
					// Validate each UUID format
					foreach ( $value as $uuid ) {
						if ( ! File::find_by_uuid( $uuid ) ) {
							return false;
						}
					}

					return true;
				},
			),
		);
	}

	/**
	 * Validates the file UUID parameter.
	 *
	 * @return array Argument definition for file UUID.
	 * @since 1.0.0
	 */
	private function validate_file_uuid(): array {
		return array(
			'uuid' => array(
				'type'              => 'string',
				'format'            => 'uuid',
				'required'          => true,
				'validate_callback' => function ( $value, $request, $param ) {
					// Validate UUID format
					if ( ! preg_match( '/^[a-f0-9\-]{36}$/i', $value ) ) {
						return false;
					}

					// Check if file exists
					$file = File::find_by_uuid( $value );

					return ! empty( $file );
				},
			),
		);
	}

	/**
	 * Validates the refresh metadata request parameters.
	 *
	 * @return array Argument definition for refresh metadata params.
	 * @since 1.0.0
	 */
	private function validate_refresh_params(): array {
		return array(
			'uuids' => array(
				'type'              => 'array',
				'required'          => true,
				'items'             => array(
					'type'   => 'string',
					'format' => 'uuid',
				),
				'validate_callback' => function ( $value, $request, $param ) {
					if ( ! is_array( $value ) || empty( $value ) ) {
						return false;
					}

					// Validate each UUID format
					foreach ( $value as $uuid ) {
						if ( ! File::find_by_uuid( $uuid ) ) {
							return false;
						}
					}

					return true;
				},
			),
		);
	}

	/**
	 * Validates the import from media request parameters.
	 *
	 * @return array Argument definition for import media params.
	 * @since 1.0.0
	 */
	private function get_import_media_params(): array {
		return array(
			'attachment_ids' => array(
				'description'       => __( 'Array of WordPress Media Library attachment IDs to import.', 'limb-chatbot' ),
				'type'              => 'array',
				'required'          => true,
				'items'             => array(
					'type' => 'integer',
				),
				'sanitize_callback' => function ( $value ) {
					if ( ! is_array( $value ) ) {
						return array();
					}

					return array_map( 'absint', $value );
				},
				'validate_callback' => function ( $value, $request, $param ) {
					if ( ! is_array( $value ) || empty( $value ) ) {
						return new WP_Error(
							'invalid_attachment_ids',
							__( 'Attachment IDs must be a non-empty array.', 'limb-chatbot' ),
							array( 'status' => 400 )
						);
					}

					// Validate each attachment exists
					foreach ( $value as $attachment_id ) {
						if ( ! wp_attachment_is_image( $attachment_id ) && ! get_attached_file( $attachment_id ) ) {
							return new WP_Error(
								'invalid_attachment_id',
								sprintf( __( 'Attachment with ID %d does not exist.', 'limb-chatbot' ), $attachment_id ),
								array( 'status' => 400 )
							);
						}
					}

					return true;
				},
			),
		);
	}

	/**
	 * Retrieves a collection of files.
	 *
	 * Supports filtering, searching, and pagination.
	 *
	 * @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 {
			// Build query parameters
			$params = array(
				'per_page' => $request->get_param( 'per_page' ) ?? 10,
				'page'     => $request->get_param( 'page' ) ?? 1,
				'orderby'  => $request->get_param( 'orderby' ) ?? 'created_at',
				'order'    => $request->get_param( 'order' ) ?? 'desc',
				'search'   => $request->get_param( 'search' ) ?? null,
			);
			if ( $status = $request->get_param( 'status' ) ) {
				$params['status'] = $status;
			}
			if ( $uuids = $request->get_param( 'uuids' ) ) {
				$params['uuids'] = $uuids;
			}
			if ( $external_provider = $request->get_param( 'external_provider' ) ) {
				$params['external_provider'] = $external_provider;
			}
			$files = $this->repository->get_items( $params );

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

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

	/**
	 * Retrieves a single file by 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 get_item( $request ) {
		try {
			$uuid = $request->get_param( 'uuid' );
			$file = $this->repository->get_item( $uuid );

			if ( ! $file ) {
				return new WP_Error(
					'file_not_found',
					__( 'File not found.', 'limb-chatbot' ),
					array( 'status' => 404 )
				);
			}

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

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

	/**
	 * Deletes a single file by 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 delete_item( $request ) {
		try {
			$uuid    = $request->get_param( 'uuid' );
			$deleted = File::delete( $uuid );

			// Return the deleted file data
			return rest_ensure_response( $deleted );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Refreshes external metadata for specified files.
	 *
	 * Fetches the latest metadata from the AI provider and updates the file records.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function refresh( $request ) {
		try {
			$uuids   = $request->get_param( 'uuids' );
			$results = $this->file_service->refresh_files( $uuids );

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

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


	/**
	 * Handles batch file upload for knowledge source files.
	 *
	 * Validates and processes multiple file uploads, saving them to the
	 * uploads/lbaic/knowledge-source-files directory.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function batch_upload( $request ) {
		try {
			 // todo merge the wp media approach
			$files          = $request->get_file_params();
			$upload_service = new File_Upload_Service( Limb_Chatbot()->get_files_dir() . Dataset::FILES_KNOWLEDGE_SUB_DIR );
			$upload_result  = $upload_service->batch_upload( $files );

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

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

	/**
	 * Imports files from WordPress Media Library.
	 *
	 * Copies selected media library files to the plugin's knowledge-source-files
	 * directory and creates File records for them.
	 *
	 * @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_from_media( $request ) {
		try {
			$attachment_ids = $request->get_param( 'attachment_ids' );
			$upload_service = new File_Upload_Service( Limb_Chatbot()->get_files_dir() . Dataset::FILES_KNOWLEDGE_SUB_DIR );
			$import_result  = $upload_service->import_from_media_library( $attachment_ids );

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

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

	/**
	 * 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' );
	}
}

