<?php

namespace Limb_Chatbot\Includes\AI_Providers\Open_Ai\Endpoints;

use Limb_Chatbot\Includes\AI_Providers\Open_Ai\Endpoints\File\Handlers\File_Response_Handler;
use Limb_Chatbot\Includes\AI_Providers\Open_Ai\Utilities\File_Utility;
use Limb_Chatbot\Includes\Data_Objects\File;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Services\File_Service;

/**
 * OpenAI File Endpoint
 *
 * Handles file upload and retrieval operations with the OpenAI API.
 * This endpoint manages the complete file lifecycle including upload,
 * retrieval, and metadata management for files used in AI operations.
 *
 * Supported Operations:
 * - File upload with purpose mapping (knowledge generation, etc.)
 * - File retrieval and content access
 * - File metadata refresh and status checking
 * - Multipart form data handling for large files
 *
 * Purpose Mapping:
 * - 'knowledge' -> 'user_data' (for RAG and knowledge generation)
 * - Other purposes passed through as-is
 *
 * @package Limb_Chatbot\Includes\AI_Providers\Open_Ai\Endpoints
 * @since 1.0.0
 */
class File_Endpoint extends Open_Ai_Endpoint {

	private static array $purposes = [
		File_Service::PURPOSE_KNOWLEDGE_GENERATION => 'user_data',
		//...
	];

	/**
	 * File_Endpoint constructor.
	 *
	 * @param  File_Utility  $utility  Utility object containing configuration and file metadata.
	 *
	 * @since 1.0.0
	 */
	public function __construct( File_Utility $utility ) {
		parent::__construct( $utility );
	}

	/**
	 * Creates a new file upload to the OpenAI API.
	 *
	 * This method handles the complete file upload process:
	 * 1. Validates file exists and is readable
	 * 2. Creates multipart form data with file content and purpose
	 * 3. Sends request to OpenAI /files endpoint
	 * 4. Returns parsed response with file metadata
	 *
	 * @return object The decoded JSON response from OpenAI API containing file metadata.
	 * @throws Exception If file is not found, cannot be read, or purpose is invalid.
	 * @since 1.0.0
	 */
	public function create() {
		$global_utility = $this->utility->global_utility;
		$http_client    = $this->http_client_factory();
		$response       = $this->handle_upload( $global_utility->file_path, $global_utility->purpose, $http_client );

		return ( new File_Response_Handler( $response, $http_client, $this ) )->get_body();
	}

	/**
	 * Handles the actual file upload to OpenAI API.
	 *
	 * Creates a multipart/form-data request with the file content and purpose.
	 * The file is read from disk, encoded in the request body, and sent to OpenAI's
	 * /files endpoint.
	 *
	 * @param  string  $file_path  Full path to the file on disk.
	 * @param  string  $purpose  Purpose of the upload (e.g., 'vision', 'assistants').
	 * @param  mixed  $http_client  HTTP client instance for making the request.
	 *
	 * @return mixed HTTP response from the OpenAI API.
	 * @throws Exception If file doesn't exist, can't be read, or purpose is invalid.
	 * @since 1.0.0
	 */
	public function handle_upload( $file_path, $purpose, $http_client ) {
		// Validate file path and existence
		if ( empty( $file_path ) || ! file_exists( $file_path ) ) {
			throw new Exception(
				Error_Codes::FILE_NOT_FOUND,
				sprintf( __( 'File does not exist: %s', 'limb-chatbot' ), basename( $file_path ) )
			);
		}

		// Validate purpose
		if ( empty( $purpose ) ) {
			throw new Exception(
				Error_Codes::VALIDATION_INVALID_VALUE,
				__( 'Invalid purpose provided for file upload.', 'limb-chatbot' )
			);
		}

		// Read file content
		$file_content = file_get_contents( $file_path );
		if ( $file_content === false ) {
			throw new Exception(
				Error_Codes::FILE_FAILED_TO_OPEN,
				sprintf( __( 'Error reading file: %s', 'limb-chatbot' ), basename( $file_path ) )
			);
		}
		// Create multipart form data
		$boundary = wp_generate_password( 24, false );
		$eol      = "\r\n";
		$body     = '';

		// Add file part
		$body .= '--' . $boundary . $eol;
		$body .= 'Content-Disposition: form-data; name="file"; filename="' . basename( $file_path ) . '"' . $eol;
		$body .= 'Content-Type: application/octet-stream' . $eol . $eol;
		$body .= $file_content . $eol;

		// Add purpose part
		$body .= '--' . $boundary . $eol;
		$body .= 'Content-Disposition: form-data; name="purpose"' . $eol . $eol;
		$body .= $purpose . $eol;

		// Close boundary
		$body .= '--' . $boundary . '--' . $eol;

		// Make API request
		return $http_client->post( self::API_BASE_URL . '/files', array(
			'headers' => $this->get_header( null, '', 'multipart/form-data; boundary=' . $boundary ),
			'body'    => $body,
			'timeout' => $this->utility->get_timeout(),
		) );
	}

	/**
	 * Retrieves the contents of a previously uploaded file from OpenAI.
	 *
	 * @return string The raw response body containing file contents.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function retrieve() {
		$http_client = $this->http_client_factory();
		$response    = $http_client->get( self::API_BASE_URL . '/files/' . $this->utility->global_utility->file_path . '/content',
			array(
				'headers' => $this->get_header(),
				'timeout' => $this->utility->get_timeout(),
			) );

		return ( new File_Response_Handler( $response, $http_client, $this ) )->get_response();
	}

	/**
	 * Uploads a File object to OpenAI and returns structured metadata.
	 *
	 * This method is the main entry point for file uploads from the File_Service.
	 * It handles purpose mapping, performs the upload, and returns structured
	 * metadata objects for storage in the File data object.
	 *
	 * Process:
	 * 1. Maps internal purpose to OpenAI's expected purpose values
	 * 2. Performs the actual file upload via handle_upload()
	 * 3. Parses response and extracts metadata
	 * 4. Returns array of Meta_Data_Object instances
	 *
	 * @param  File  $file  The File object to upload.
	 *
	 * @return array Array of Meta_Data_Object instances with file metadata including:
	 *               - external_id: OpenAI file ID
	 *               - status: Upload status
	 *               - bytes: File size in bytes
	 *               - created_at: Upload timestamp
	 * @throws Exception If upload fails, response is invalid, or file processing fails.
	 * @since 1.0.0
	 */
	public function upload( File $file ) {
		$global_utility = $this->utility->global_utility;
		$http_client    = $this->http_client_factory();

		// Map purpose to OpenAI's expected values
		$should_map = array_key_exists( $global_utility->purpose, static::$purposes );
		$purpose    = $should_map ? static::$purposes[ $global_utility->purpose ] : $global_utility->purpose;

		// Perform upload
		$response = $this->handle_upload( $global_utility->file_path, $purpose, $http_client );

		// Parse response and extract metadata
		return ( new File_Response_Handler( $response, $http_client, $this ) )->get_file_metadata();
	}

	/**
	 * Refreshes file metadata from OpenAI API.
	 *
	 * Retrieves the latest metadata for a file from OpenAI and returns
	 * updated metadata objects. This is useful for checking upload status,
	 * file availability, or other provider-specific information.
	 *
	 * @param File $file The File object to refresh metadata for.
	 * @return array Array of Meta_Data_Object instances with updated metadata.
	 * @throws Exception If file ID is missing, API request fails, or response is invalid.
	 * @since 1.0.0
	 */
	public function refresh( File $file ) {
		$file_id = $file->get_meta( 'external_id' );
		$http_client = $this->http_client_factory();
		$response = $http_client->get( self::API_BASE_URL . '/files/' . $file_id,
			array(
				'headers' => $this->get_header(),
				'timeout' => $this->utility->get_timeout(),
			) );

		return ( new File_Response_Handler( $response, $http_client, $this ) )->get_file_metadata();
	}
}