<?php

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

use Limb_Chatbot\Includes\AI_Providers\Claude\Endpoints\Claude_Endpoint;
use Limb_Chatbot\Includes\AI_Providers\Claude\Endpoints\File\Handlers\File_Response_Handler;
use Limb_Chatbot\Includes\Data_Objects\File;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;

/**
 * Class File_Endpoint
 *
 * Handles Claude Files API interactions for file upload, retrieval, and management.
 *
 * Claude Files API (Beta):
 * - Upload: POST /v1/files
 * - List: GET /v1/files
 * - Retrieve: GET /v1/files/{file_id}
 * - Download: GET /v1/files/{file_id}/content
 * - Delete: DELETE /v1/files/{file_id}
 *
 * @package Limb_Chatbot\Includes\AI_Providers\Claude\Endpoints\File
 * @since 1.0.9
 */
class File_Endpoint extends Claude_Endpoint {

	/**
	 * Beta header required for Files API.
	 *
	 * @var string
	 * @since 1.0.9
	 */
	const FILES_API_BETA = 'files-api-2025-04-14';

	/**
	 * PDF beta header for PDF support.
	 *
	 * @var string
	 * @since 1.0.9
	 */
	const PDF_BETA = 'pdfs-2024-09-25';

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

	/**
	 * Get headers with Files API beta.
	 *
	 * @return array Headers array.
	 * @since 1.0.9
	 */
	protected function get_file_api_headers(): array {
		return array(
			'x-api-key'         => $this->get_auth_key(),
			'anthropic-version' => self::API_VERSION,
			'anthropic-beta'    => self::FILES_API_BETA,
		);
	}

	/**
	 * Upload a file to Claude Files API.
	 *
	 * @param  File  $file  The File object to upload.
	 *
	 * @return array File metadata from the response.
	 * @throws Exception If upload fails.
	 * @since 1.0.9
	 */
	public function upload( File $file ): array {
		$file_path = $this->utility->global_utility->file_path;

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

		$http_client = $this->http_client_factory();

		// Build multipart form data
		$boundary = wp_generate_password( 24, false );
		$body     = $this->build_multipart_body( $boundary, $file_content, $file->get_mime_type(),
			basename( $file_path ) );

		$headers = array(
			'x-api-key'         => $this->get_auth_key(),
			'anthropic-version' => self::API_VERSION,
			'anthropic-beta'    => self::FILES_API_BETA,
			'Content-Type'      => "multipart/form-data; boundary={$boundary}",
		);

		$response = $http_client->post(
			self::API_BASE_URL . '/files',
			array(
				'headers' => $headers,
				'body'    => $body,
				'timeout' => $this->utility->get_timeout(),
			)
		);

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

	/**
	 * Build multipart form data for file upload.
	 *
	 * @param  string  $boundary  The boundary string.
	 * @param  string  $file_content  The file content.
	 * @param  string  $mime_type  The file's MIME type.
	 * @param  string  $filename  The filename.
	 *
	 * @return string The formatted multipart body.
	 * @since 1.0.9
	 */
	private function build_multipart_body(
		string $boundary,
		string $file_content,
		string $mime_type,
		string $filename
	): string {
		$body = "--{$boundary}\r\n";
		$body .= "Content-Disposition: form-data; name=\"file\"; filename=\"{$filename}\"\r\n";
		$body .= "Content-Type: {$mime_type}\r\n\r\n";
		$body .= $file_content . "\r\n";
		$body .= "--{$boundary}--";

		return $body;
	}

	/**
	 * List files from Claude Files API.
	 *
	 * @param  int  $limit  Number of items to return (1-1000).
	 * @param  string|null  $after_id  Cursor for pagination (after this ID).
	 * @param  string|null  $before_id  Cursor for pagination (before this ID).
	 *
	 * @return array List of file metadata.
	 * @throws Exception If request fails.
	 * @since 1.0.9
	 */
	public function list( int $limit = 20, ?string $after_id = null, ?string $before_id = null ): array {
		$http_client = $this->http_client_factory();

		$query_params = array( 'limit' => $limit );
		if ( $after_id ) {
			$query_params['after_id'] = $after_id;
		}
		if ( $before_id ) {
			$query_params['before_id'] = $before_id;
		}

		$url = self::API_BASE_URL . '/files?' . http_build_query( $query_params );

		$response = $http_client->get(
			$url,
			array(
				'headers' => $this->get_file_api_headers(),
				'timeout' => $this->utility->get_timeout(),
			)
		);

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

	/**
	 * Retrieve file metadata.
	 *
	 * @param  string  $file_id  The file ID.
	 *
	 * @return array File metadata.
	 * @throws Exception If request fails.
	 * @since 1.0.9
	 */
	public function retrieve( string $file_id ): array {
		$http_client = $this->http_client_factory();

		$response = $http_client->get(
			self::API_BASE_URL . '/files/' . $file_id,
			array(
				'headers' => $this->get_file_api_headers(),
				'timeout' => $this->utility->get_timeout(),
			)
		);

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

	/**
	 * Download file content.
	 *
	 * @param  string  $file_id  The file ID.
	 *
	 * @return string Raw file content.
	 * @throws Exception If request fails.
	 * @since 1.0.9
	 */
	public function download( string $file_id ): string {
		$http_client = $this->http_client_factory();

		$response = $http_client->get(
			self::API_BASE_URL . '/files/' . $file_id . '/content',
			array(
				'headers' => $this->get_file_api_headers(),
				'timeout' => $this->utility->get_timeout(),
			)
		);

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

	/**
	 * Delete a file.
	 *
	 * @param  string  $file_id  The file ID.
	 *
	 * @return array Deletion confirmation.
	 * @throws Exception If request fails.
	 * @since 1.0.9
	 */
	public function delete( string $file_id ): array {
		$response = wp_remote_request(
			self::API_BASE_URL . '/files/' . $file_id,
			array(
				'method'  => 'DELETE',
				'headers' => $this->get_file_api_headers(),
				'timeout' => $this->utility->get_timeout(),
			)
		);

		$http_client = new \WP_Http();

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

	/**
	 * Refresh file metadata (check status).
	 *
	 * @param  File  $file  The file object to refresh.
	 *
	 * @return array Updated file metadata.
	 * @throws Exception If request fails.
	 * @since 1.0.9
	 */
	public function refresh( File $file ): array {
		$file_id = $file->get_meta( 'external_id' );

		if ( empty( $file_id ) ) {
			throw new Exception(
				Error_Codes::FILE_NOT_FOUND,
				__( 'File does not have an external ID', 'limb-chatbot' )
			);
		}

		return $this->retrieve( $file_id );
	}
}
