<?php

namespace Limb_Chatbot\Includes\AI_Providers\Gemini\Endpoints\File;

use Limb_Chatbot\Includes\AI_Providers\Gemini\Endpoints\File\Handlers\File_Response_Handler;
use Limb_Chatbot\Includes\AI_Providers\Gemini\Endpoints\Gemini_Endpoint;
use Limb_Chatbot\Includes\AI_Providers\Gemini\Utilities\File_Utility;
use Limb_Chatbot\Includes\Data_Objects\File;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;

/**
 * Class File_Endpoint
 *
 * Handles Gemini file upload and retrieval operations.
 *
 * @package Limb_Chatbot\Includes\AI_Providers\Gemini\Endpoints\File
 * @since 1.0.0
 */
class File_Endpoint extends Gemini_Endpoint {


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

	/**
	 * Retrieves the contents of a previously uploaded file from Gemini.
	 *
	 * @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 . 'v1beta/files/' . $this->utility->global_utility->file_path . '/content',
			array(
				'headers' => $this->get_header( true ),
				'timeout' => $this->utility->get_timeout(),
			) );

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

	/**
	 * Uploads a File object to Gemini.
	 *
	 * Maps the internal purpose to Gemini's expected purpose values and
	 * delegates to handle_upload(). Returns metadata from the response.
	 *
	 * @param  File  $file  The File object to upload.
	 *
	 * @return array Array of Meta_Data_Object instances with file metadata.
	 * @throws Exception If upload fails or response is invalid.
	 * @since 1.0.0
	 */
	public function upload( File $file ) {
		$global_utility = $this->utility->global_utility;
		$http_client    = $this->http_client_factory();
		// Perform upload
		$response = $this->handle_upload( $global_utility->file_path, $http_client, $file->get_mime_type() );

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

	/**
	 * Handles the actual file upload to Gemini API.
	 *
	 * Creates a multipart/related request with metadata and file content.
	 * The file is read from disk, encoded in the request body, and sent to Gemini
	 *
	 * @param  string  $file_path  Full path to the file on disk.
	 * @param  mixed  $http_client  HTTP client instance for making the request.
	 *
	 * @return mixed HTTP response from the Gemini 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, $http_client, $mime_type = '' ) {
		// 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 ) ) );
		}

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

		// Build multipart body according to Gemini API specification
		$boundary = wp_generate_password( 24, false );
		$body     = $this->build_multipart_body( $boundary, $file_content, $mime_type );

		// Get headers
		$headers = [
			'Content-Type'   => "multipart/related; boundary={$boundary}",
			'X-Goog-Api-Key' => $this->get_auth_key()
		];

		// Make API request
		return $http_client->post( self::API_BASE_URL . '/upload/v1beta/files?uploadType=multipart', array(
			'headers' => $headers,
			'body'    => $body,
			'timeout' => $this->utility->get_timeout()
		) );
	}

	/**
	 * Build multipart form data for file upload according to Gemini API specification.
	 *
	 * @param  string  $boundary  The boundary string.
	 * @param  string  $file_content  The file content.
	 * @param  string  $mime_type  The file's MIME type.
	 *
	 * @return string The formatted multipart body.
	 * @since 1.0.0
	 */
	private function build_multipart_body( string $boundary, string $file_content, string $mime_type ): string {
		// Metadata must be empty JSON (required by Gemini API)
		$metadata_json = '{}';

		$body = "--{$boundary}\r\n";
		$body .= "Content-Type: application/json; charset=UTF-8\r\n\r\n";
		$body .= $metadata_json . "\r\n";
		$body .= "--{$boundary}\r\n";
		$body .= "Content-Type: {$mime_type}\r\n\r\n";
		$body .= $file_content . "\r\n";
		$body .= "--{$boundary}--";

		return $body;
	}

	public function refresh( File $file ) {
		$file_id     = $file->get_meta( 'external_id' );
		$http_client = $this->http_client_factory();
		$headers = array(
			'X-Goog-Api-Key' => $this->get_auth_key(),
			'Content-Type' => 'application/json',
		);
		$response    = $http_client->get( self::API_BASE_URL.'v1beta/' . $file_id,
			array(
				'headers' => $headers,
				'timeout' => $this->utility->get_timeout(),
			) );

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