<?php
/**
 * Image search and selection REST endpoints
 *
 * @package Simple_Ai_Auto_Post
 */

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Register REST API routes for image search and selection
 */
function saiap_register_image_routes() {
	register_rest_route(
		'simple-ai-auto-post/v1',
		'/image-search',
		array(
			'methods'             => 'GET',
			'callback'            => 'saiap_handle_image_search',
			'permission_callback' => function () {
				return current_user_can( 'edit_posts' );
			},
			'args'                => array(
				'q'   => array(
					'required' => true,
					'type'     => 'string',
				),
				'num' => array(
					'required' => false,
					'type'     => 'integer',
					'default'  => 3,
				),
			),
		)
	);

	register_rest_route(
		'simple-ai-auto-post/v1',
		'/image-select',
		array(
			'methods'             => 'POST',
			'callback'            => 'saiap_handle_image_select',
			'permission_callback' => function () {
				return current_user_can( 'edit_posts' );
			},
			'args'                => array(
				'postId'            => array(
					'required' => true,
					'type'     => 'integer',
				),
				'imageUrl'          => array(
					'required' => true,
					'type'     => 'string',
				),
				'alt'               => array(
					'required' => false,
					'type'     => 'string',
					'default'  => '',
				),
				'sourceAttribution' => array(
					'required' => false,
					'type'     => 'object',
					'default'  => array(),
				),
			),
		)
	);

	// =========================================================================
	// Image Agent Endpoints (HMAC-protected, called by premium backend)
	// =========================================================================

	// GET /posts/{id}/content - Get post block structure with heading coordinates
	register_rest_route(
		'simple-ai-auto-post/v1',
		'/posts/(?P<id>\d+)/content',
		array(
			'methods'             => 'GET',
			'callback'            => 'saiap_handle_get_post_content',
			'permission_callback' => '__return_true', // HMAC auth in handler
			'args'                => array(
				'id'        => array(
					'required'          => true,
					'type'              => 'integer',
					'validate_callback' => function ( $value ) {
						return is_numeric( $value ) && $value > 0;
					},
				),
				'summary'   => array(
					'required' => false,
					'type'     => 'boolean',
					'default'  => false,
				),
				'timestamp' => array(
					'required' => true,
					'type'     => 'integer',
				),
				'token'     => array(
					'required' => true,
					'type'     => 'string',
				),
			),
		)
	);

	// POST /posts/{id}/feature-image - Set feature image for post
	register_rest_route(
		'simple-ai-auto-post/v1',
		'/posts/(?P<id>\d+)/feature-image',
		array(
			'methods'             => 'POST',
			'callback'            => 'saiap_handle_set_feature_image',
			'permission_callback' => '__return_true', // HMAC auth in handler
			'args'                => array(
				'id'        => array(
					'required'          => true,
					'type'              => 'integer',
					'validate_callback' => function ( $value ) {
						return is_numeric( $value ) && $value > 0;
					},
				),
				'image_url' => array(
					'required' => true,
					'type'     => 'string',
				),
				'alt_text'  => array(
					'required' => false,
					'type'     => 'string',
					'default'  => '',
				),
				'timestamp' => array(
					'required' => true,
					'type'     => 'integer',
				),
				'token'     => array(
					'required' => true,
					'type'     => 'string',
				),
			),
		)
	);

	// POST /posts/{id}/inline-image - Insert inline image at selector position
	register_rest_route(
		'simple-ai-auto-post/v1',
		'/posts/(?P<id>\d+)/inline-image',
		array(
			'methods'             => 'POST',
			'callback'            => 'saiap_handle_insert_inline_image',
			'permission_callback' => '__return_true', // HMAC auth in handler
			'args'                => array(
				'id'            => array(
					'required'          => true,
					'type'              => 'integer',
					'validate_callback' => function ( $value ) {
						return is_numeric( $value ) && $value > 0;
					},
				),
				'image_url'     => array(
					'required' => true,
					'type'     => 'string',
				),
				'after_heading' => array(
					'required' => true,
					'type'     => 'object', // { level: int, occurrence: int }
				),
				'position'      => array(
					'required' => false,
					'type'     => 'string',
					'default'  => 'after_heading',
					'enum'     => array( 'after_heading', 'after_first_paragraph' ),
				),
				'alt_text'      => array(
					'required' => false,
					'type'     => 'string',
					'default'  => '',
				),
				'caption'       => array(
					'required' => false,
					'type'     => 'string',
					'default'  => '',
				),
				'timestamp'     => array(
					'required' => true,
					'type'     => 'integer',
				),
				'token'         => array(
					'required' => true,
					'type'     => 'string',
				),
			),
		)
	);

	// GET /posts/{id}/image-agent-credentials
	// Returns fresh HMAC credentials so the backend can run image agent on an
	// existing post on demand (e.g. retroactive fix, admin tooling).
	// Authenticated via X-License-Key header — same as insert-post endpoint.
	register_rest_route(
		'simple-ai-auto-post/v1',
		'/posts/(?P<id>\d+)/image-agent-credentials',
		array(
			'methods'             => 'GET',
			'callback'            => 'saiap_handle_get_image_agent_credentials',
			'permission_callback' => 'saiap_verify_insert_post_permission', // reuse license-key check
			'args'                => array(
				'id' => array(
					'required'          => true,
					'type'              => 'integer',
					'validate_callback' => function ( $value ) {
						return is_numeric( $value ) && $value > 0;
					},
				),
			),
		)
	);
}
add_action( 'rest_api_init', 'saiap_register_image_routes' );

/**
 * Build image search URL
 *
 * @param string $q   Query
 * @param int    $num Number of results (max 5 on premium)
 * @param array  $exclude_urls URLs to exclude from search results.
 * @return string URL
 */
function saiap_build_premium_image_search_url( $q, $num = 3, $exclude_urls = array() ) {
	$license_key = get_option( 'saiap_license_key' );
	$domain      = function_exists( 'saiap_get_server_host' ) ? saiap_get_server_host() : wp_parse_url( home_url(), PHP_URL_HOST );

	$exclude = array();
	if ( ! empty( $exclude_urls ) && is_array( $exclude_urls ) ) {
		$exclude = array_slice(
			array_values(
				array_unique(
					array_filter(
						array_map(
							static function ( $url ) {
								return sanitize_text_field( (string) $url );
							},
							$exclude_urls
						)
					)
				)
			),
			0,
			15 // cap to keep query size manageable.
		);
	}

	$args = array(
		'q'           => (string) $q,
		'num'         => min( absint( $num ) ?: 3, 5 ),
		'license_key' => (string) $license_key,
		'domain'      => (string) $domain,
	);

	if ( ! empty( $exclude ) ) {
		$args['exclude'] = $exclude;
	}

	return add_query_arg( $args, 'https://saiap.gopurposego.com/api/images/search' );
}

/**
 * Programmatic image search helper
 *
 * @param string $query
 * @param int    $num
 * @param array  $exclude_urls URLs to exclude (already used images).
 * @return array|WP_Error Normalized results or error
 */
function saiap_image_search( $query, $num = 3, $exclude_urls = array() ) {
	$q = sanitize_text_field( (string) $query );
	if ( $q === '' ) {
		return new WP_Error( 'invalid_query', __( 'Missing query', 'clearpost-simple-ai-auto-post' ) );
	}

	$url = saiap_build_premium_image_search_url( $q, $num, $exclude_urls );
	$response = wp_remote_get( $url, array( 'timeout' => 15 ) );
	if ( is_wp_error( $response ) ) {
		return new WP_Error( 'remote_error', $response->get_error_message() );
	}

	$code = wp_remote_retrieve_response_code( $response );
	$body = json_decode( wp_remote_retrieve_body( $response ), true );
	if ( 200 !== $code || ! isset( $body['results'] ) || ! is_array( $body['results'] ) ) {
		$message = isset( $body['error'] ) ? $body['error'] : __( 'Image search failed', 'clearpost-simple-ai-auto-post' );
		return new WP_Error( 'bad_response', $message );
	}

	$sanitized = array();
	foreach ( array_slice( $body['results'], 0, min( absint( $num ) ?: 3, 3 ) ) as $item ) {
		$sanitized[] = array(
			'thumbnailUrl'  => esc_url_raw( $item['thumbnailUrl'] ?? '' ),
			'fullImageUrl'  => esc_url_raw( $item['fullImageUrl'] ?? '' ),
			'title'         => sanitize_text_field( $item['title'] ?? '' ),
			'sourcePageUrl' => esc_url_raw( $item['sourcePageUrl'] ?? '' ),
		);
	}

	return $sanitized;
}

/**
 * Generate a SHA256 hash for a file path.
 *
 * @param string $file_path
 * @return string|null
 */
function saiap_generate_file_hash( $file_path ) {
	$file_path = (string) $file_path;
	if ( '' === $file_path || ! file_exists( $file_path ) ) {
		return null;
	}

	$hash = hash_file( 'sha256', $file_path );
	return $hash ? $hash : null;
}

/**
 * Detect actual MIME type from file content and return appropriate extension.
 *
 * This solves the problem where Unsplash URLs claim one format (e.g., fm=jpg)
 * but may serve a different format (e.g., WebP). WordPress validates that file
 * content matches the claimed extension, so we detect the actual type and use
 * the correct extension.
 *
 * @param string $file_path Path to the downloaded file.
 * @return string File extension (without dot), e.g., 'jpg', 'png', 'webp'.
 */
function saiap_detect_image_extension( $file_path ) {
	$mime_to_ext = array(
		'image/jpeg' => 'jpg',
		'image/png'  => 'png',
		'image/gif'  => 'gif',
		'image/webp' => 'webp',
	);

	$detected_mime = null;

	// Try finfo first (most reliable).
	if ( function_exists( 'finfo_open' ) ) {
		$finfo = finfo_open( FILEINFO_MIME_TYPE );
		if ( $finfo ) {
			$detected_mime = finfo_file( $finfo, $file_path );
			finfo_close( $finfo );
		}
	}

	// Fall back to WordPress function.
	if ( ! $detected_mime && function_exists( 'wp_get_image_mime' ) ) {
		$detected_mime = wp_get_image_mime( $file_path );
	}

	// Return extension for detected MIME, or default to jpg.
	if ( $detected_mime && isset( $mime_to_ext[ $detected_mime ] ) ) {
		return $mime_to_ext[ $detected_mime ];
	}

	return 'jpg';
}

/**
 * Download image and set as featured image for a post (helper)
 *
 * @param int    $post_id
 * @param string $image_url
 * @param string $alt
 * @param array  $attrib { sourcePageUrl?: string, title?: string }
 * @return int|WP_Error Attachment ID or error
 */
function saiap_set_featured_image_from_url( $post_id, $image_url, $alt = '', $attrib = array() ) {
	$post_id  = absint( $post_id );
	$imageUrl = esc_url_raw( (string) $image_url );
	$alt      = sanitize_text_field( (string) $alt );
	$attrib   = is_array( $attrib ) ? $attrib : array();

	if ( ! $post_id || ! get_post( $post_id ) ) {
		return new WP_Error( 'invalid_post', __( 'Invalid post', 'clearpost-simple-ai-auto-post' ) );
	}
	if ( empty( $imageUrl ) ) {
		return new WP_Error( 'invalid_url', __( 'Invalid image URL', 'clearpost-simple-ai-auto-post' ) );
	}

	// Sideload
	require_once ABSPATH . 'wp-admin/includes/file.php';
	require_once ABSPATH . 'wp-admin/includes/media.php';
	require_once ABSPATH . 'wp-admin/includes/image.php';

	$tmp = download_url( $imageUrl );
	if ( is_wp_error( $tmp ) ) {
		return new WP_Error( 'download_failed', $tmp->get_error_message() );
	}

	// Detect actual MIME type from file content and use correct extension.
	// This fixes issues where Unsplash serves WebP when URL claims jpg.
	$ext = saiap_detect_image_extension( $tmp );

	// Build filename from URL path, using detected extension.
	$url_path     = wp_parse_url( $imageUrl, PHP_URL_PATH );
	$base_name    = basename( $url_path ) ?: 'image';
	$base_name    = preg_replace( '/\.\w+$/', '', $base_name ); // Strip any existing extension.
	$filename     = $base_name . '.' . $ext;

	$file_array = array(
		'name'     => $filename,
		'tmp_name' => $tmp,
	);

	$overrides = array( 'test_form' => false );
	$results   = wp_handle_sideload( $file_array, $overrides );
	if ( isset( $results['error'] ) ) {
		wp_delete_file( $tmp );
		return new WP_Error( 'sideload_failed', $results['error'] );
	}

	$filename    = $results['file'];
	$wp_filetype = wp_check_filetype( $filename, null );
	$attachment  = array(
		'post_mime_type' => $wp_filetype['type'],
		'post_title'     => sanitize_file_name( wp_basename( $filename ) ),
		'post_content'   => '',
		'post_status'    => 'inherit',
	);
	$attach_id = wp_insert_attachment( $attachment, $filename, $post_id );
	if ( is_wp_error( $attach_id ) ) {
		return new WP_Error( 'attachment_failed', $attach_id->get_error_message() );
	}

	$attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
	wp_update_attachment_metadata( $attach_id, $attach_data );

	$hash = saiap_generate_file_hash( $filename );
	if ( $hash ) {
		update_post_meta( $attach_id, '_saiap_image_hash', $hash );
	}

	if ( ! empty( $alt ) ) {
		update_post_meta( $attach_id, '_wp_attachment_image_alt', $alt );
	}
	if ( is_array( $attrib ) ) {
		$source_url   = isset( $attrib['sourcePageUrl'] ) ? esc_url_raw( $attrib['sourcePageUrl'] ) : '';
		$source_title = isset( $attrib['title'] ) ? sanitize_text_field( $attrib['title'] ) : '';
		if ( $source_url ) {
			update_post_meta( $attach_id, '_saiap_image_source_url', $source_url );
		}
		if ( $source_title ) {
			update_post_meta( $attach_id, '_saiap_image_source_title', $source_title );
		}
	}

	set_post_thumbnail( $post_id, $attach_id );

	return $attach_id;
}

/**
 * Download image and create attachment for inline use (does NOT set featured image)
 *
 * @param int    $post_id
 * @param string $image_url
 * @param string $alt
 * @param array  $attrib { sourcePageUrl?: string, title?: string }
 * @return int|WP_Error Attachment ID or error
 */
function saiap_create_image_attachment_from_url( $post_id, $image_url, $alt = '', $attrib = array() ) {
	$post_id  = absint( $post_id );
	$imageUrl = esc_url_raw( (string) $image_url );
	$alt      = sanitize_text_field( (string) $alt );
	$attrib   = is_array( $attrib ) ? $attrib : array();

	if ( ! $post_id || ! get_post( $post_id ) ) {
		return new WP_Error( 'invalid_post', __( 'Invalid post', 'clearpost-simple-ai-auto-post' ) );
	}
	if ( empty( $imageUrl ) ) {
		return new WP_Error( 'invalid_url', __( 'Invalid image URL', 'clearpost-simple-ai-auto-post' ) );
	}

	require_once ABSPATH . 'wp-admin/includes/file.php';
	require_once ABSPATH . 'wp-admin/includes/media.php';
	require_once ABSPATH . 'wp-admin/includes/image.php';

	$tmp = download_url( $imageUrl );
	if ( is_wp_error( $tmp ) ) {
		return new WP_Error( 'download_failed', $tmp->get_error_message() );
	}

	// Detect actual MIME type from file content and use correct extension.
	// This fixes issues where Unsplash serves WebP when URL claims jpg.
	$ext = saiap_detect_image_extension( $tmp );

	// Build filename from URL path, using detected extension.
	$url_path     = wp_parse_url( $imageUrl, PHP_URL_PATH );
	$base_name    = basename( $url_path ) ?: 'image';
	$base_name    = preg_replace( '/\.\w+$/', '', $base_name ); // Strip any existing extension.
	$filename     = $base_name . '.' . $ext;

	$file_array = array(
		'name'     => $filename,
		'tmp_name' => $tmp,
	);

	$overrides = array( 'test_form' => false );
	$results   = wp_handle_sideload( $file_array, $overrides );
	if ( isset( $results['error'] ) ) {
		wp_delete_file( $tmp );
		return new WP_Error( 'sideload_failed', $results['error'] );
	}

	$filename    = $results['file'];
	$wp_filetype = wp_check_filetype( $filename, null );
	$attachment  = array(
		'post_mime_type' => $wp_filetype['type'],
		'post_title'     => sanitize_file_name( wp_basename( $filename ) ),
		'post_content'   => '',
		'post_status'    => 'inherit',
		'post_parent'    => $post_id,
	);
	$attach_id = wp_insert_attachment( $attachment, $filename, $post_id );
	if ( is_wp_error( $attach_id ) ) {
		return new WP_Error( 'attachment_failed', $attach_id->get_error_message() );
	}

	$attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
	wp_update_attachment_metadata( $attach_id, $attach_data );

	$hash = saiap_generate_file_hash( $filename );
	if ( $hash ) {
		update_post_meta( $attach_id, '_saiap_image_hash', $hash );
	}

	if ( ! empty( $alt ) ) {
		update_post_meta( $attach_id, '_wp_attachment_image_alt', $alt );
	}
	if ( is_array( $attrib ) ) {
		$source_url   = isset( $attrib['sourcePageUrl'] ) ? esc_url_raw( $attrib['sourcePageUrl'] ) : '';
		$source_title = isset( $attrib['title'] ) ? sanitize_text_field( $attrib['title'] ) : '';
		if ( $source_url ) {
			update_post_meta( $attach_id, '_saiap_image_source_url', $source_url );
		}
		if ( $source_title ) {
			update_post_meta( $attach_id, '_saiap_image_source_title', $source_title );
		}
	}

	return $attach_id;
}

/**
 * Sideload any externally hosted images referenced in post content.
 *
 * The editor chat can add images two ways: through structured image_actions,
 * which already sideload, or by writing <img> markup straight into the edited
 * content. wp_kses_post() allows an external src, so the second path leaves the
 * image hotlinked to its source instead of served from this site.
 *
 * This pass runs over the final content and fixes it regardless of how the URL
 * arrived: download each off-host image into the media library, then rewrite the
 * src to the local attachment URL.
 *
 * A URL that fails to download is left untouched. A hotlinked image is better
 * than a broken one.
 *
 * @param int    $post_id Post the attachments belong to.
 * @param string $content Post content, possibly containing external <img> tags.
 * @return string Content with external image URLs replaced by local ones.
 */
function saiap_sideload_external_images_in_content( $post_id, $content ) {
	$post_id = absint( $post_id );

	if ( ! $post_id || empty( $content ) || false === stripos( $content, '<img' ) ) {
		return $content;
	}

	if ( ! preg_match_all( '/<img\b[^>]*?\bsrc=["\']([^"\']+)["\']/i', $content, $matches ) ) {
		return $content;
	}

	$home_host = wp_parse_url( home_url(), PHP_URL_HOST );
	$replaced  = array();

	foreach ( array_unique( $matches[1] ) as $image_url ) {
		// Only touch absolute http(s) URLs pointing somewhere other than this site.
		if ( ! preg_match( '#^https?://#i', $image_url ) ) {
			continue;
		}

		$host = wp_parse_url( $image_url, PHP_URL_HOST );
		if ( ! $host || strtolower( $host ) === strtolower( (string) $home_host ) ) {
			continue;
		}

		if ( isset( $replaced[ $image_url ] ) ) {
			continue;
		}

		$attach_id = saiap_create_image_attachment_from_url( $post_id, $image_url, '' );
		if ( is_wp_error( $attach_id ) || ! $attach_id ) {
			continue;
		}

		$local_url = wp_get_attachment_url( $attach_id );
		if ( ! $local_url ) {
			continue;
		}

		$replaced[ $image_url ] = $local_url;
	}

	if ( empty( $replaced ) ) {
		return $content;
	}

	return str_replace( array_keys( $replaced ), array_values( $replaced ), $content );
}

/**
 * Attach first image result from a query
 *
 * @param int    $post_id
 * @param string $query
 * @return int|WP_Error Attachment ID or error
 */
function saiap_attach_first_image_result( $post_id, $query ) {
	$results = saiap_image_search( $query, 3 );
	if ( is_wp_error( $results ) ) {
		return $results;
	}
	foreach ( (array) $results as $item ) {
		if ( ! empty( $item['fullImageUrl'] ) ) {
			return saiap_set_featured_image_from_url(
				$post_id,
				$item['fullImageUrl'],
				isset( $item['title'] ) ? $item['title'] : '',
				array(
					'sourcePageUrl' => isset( $item['sourcePageUrl'] ) ? $item['sourcePageUrl'] : '',
					'title'         => isset( $item['title'] ) ? $item['title'] : '',
				)
			);
		}
	}
	return new WP_Error( 'no_image_found', __( 'No valid images found', 'clearpost-simple-ai-auto-post' ) );
}

/**
 * Image search
 *
 * @param WP_REST_Request $request Request.
 * @return WP_REST_Response|WP_Error
 */
function saiap_handle_image_search( WP_REST_Request $request ) {
	$q   = sanitize_text_field( $request->get_param( 'q' ) );
	$num = absint( $request->get_param( 'num' ) ) ?: 3;
	if ( empty( $q ) ) {
		return new WP_Error( 'invalid_query', __( 'Missing query', 'clearpost-simple-ai-auto-post' ), array( 'status' => 400 ) );
	}

	$results = saiap_image_search( $q, $num );
	if ( is_wp_error( $results ) ) {
		return new WP_Error( $results->get_error_code(), $results->get_error_message(), array( 'status' => 502 ) );
	}
	return rest_ensure_response( array( 'results' => $results ) );
}

/**
 * Download image and set as featured image for a post
 *
 * @param WP_REST_Request $request Request.
 * @return WP_REST_Response|WP_Error
 */
function saiap_handle_image_select( WP_REST_Request $request ) {
	$post_id  = absint( $request->get_param( 'postId' ) );
	$imageUrl = esc_url_raw( $request->get_param( 'imageUrl' ) );
	$alt      = sanitize_text_field( $request->get_param( 'alt' ) );
	$attrib   = $request->get_param( 'sourceAttribution' );

	if ( ! $post_id || ! get_post( $post_id ) ) {
		return new WP_Error( 'invalid_post', __( 'Invalid post', 'clearpost-simple-ai-auto-post' ), array( 'status' => 400 ) );
	}
	if ( empty( $imageUrl ) ) {
		return new WP_Error( 'invalid_url', __( 'Invalid image URL', 'clearpost-simple-ai-auto-post' ), array( 'status' => 400 ) );
	}

	$attach_id = saiap_set_featured_image_from_url( $post_id, $imageUrl, $alt, is_array( $attrib ) ? $attrib : array() );
	if ( is_wp_error( $attach_id ) ) {
		return new WP_Error( $attach_id->get_error_code(), $attach_id->get_error_message(), array( 'status' => 500 ) );
	}
	return rest_ensure_response( array( 'attachmentId' => $attach_id ) );
}

/**
 * Build a core/image block HTML string for a given attachment.
 *
 * @param int    $attachment_id Attachment ID.
 * @param string $alt           Alt text override (optional).
 * @param string $size_slug     Image size slug.
 * @return string
 */
function saiap_build_core_image_block_html( $attachment_id, $alt = '', $size_slug = 'large' ) {
	$attachment_id = absint( $attachment_id );
	if ( ! $attachment_id ) {
		return '';
	}

	if ( $alt !== '' ) {
		update_post_meta( $attachment_id, '_wp_attachment_image_alt', sanitize_text_field( $alt ) );
	}

	$image_src = wp_get_attachment_image_url( $attachment_id, $size_slug );
	if ( ! $image_src ) {
		return '';
	}

	$alt_text = $alt !== '' ? $alt : get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );

	$img_html = sprintf(
		'<img src="%s" alt="%s" class="wp-image-%d" />',
		esc_url( $image_src ),
		esc_attr( $alt_text ),
		$attachment_id
	);

	// Wrap image in canonical Gutenberg figure markup for core/image blocks.
	$figure_classes = array( 'wp-block-image' );
	if ( is_string( $size_slug ) && '' !== $size_slug ) {
		$figure_classes[] = 'size-' . sanitize_html_class( $size_slug );
	}
	$figure_html = '<figure class="' . esc_attr( implode( ' ', $figure_classes ) ) . '">' . $img_html . '</figure>';

	$attrs = array(
		'id'            => $attachment_id,
		'sizeSlug'      => $size_slug,
		'linkDestination' => 'none',
	);

	$block_open = '<!-- wp:image ' . wp_json_encode( $attrs ) . ' -->';
	$block_close = '<!-- /wp:image -->';

	return $block_open . $figure_html . $block_close;
}

/**
 * Insert a block into content based on a structured selector.
 *
 * @param string $content
 * @param array  $selector
 * @param string $position
 * @param string $block_html
 * @param int    $paragraph_occurrence
 * @return string|WP_Error
 */
function saiap_insert_block_by_selector( $content, $selector, $position, $block_html, $paragraph_occurrence = 1 ) {
	if ( ! is_string( $content ) || '' === $content ) {
		return $content;
	}
	$blocks = parse_blocks( $content );
	if ( ! is_array( $blocks ) ) {
		return $content;
	}

	$insertion_index = saiap_resolve_block_insertion_index( $blocks, $selector, $position, $paragraph_occurrence );
	if ( null === $insertion_index ) {
		return $content;
	}

	$new_blocks = parse_blocks( $block_html );
	if ( empty( $new_blocks ) ) {
		return $content;
	}

	array_splice( $blocks, $insertion_index, 0, $new_blocks );

	return serialize_blocks( $blocks );
}

/**
 * Resolve a selector + position to a concrete block insertion index.
 *
 * @param array $blocks Parsed blocks.
 * @param array $selector
 * @param string $position
 * @param int    $paragraph_occurrence
 * @return int|null
 */
function saiap_resolve_block_insertion_index( $blocks, $selector, $position, $paragraph_occurrence = 1 ) {
	if ( ! is_array( $blocks ) ) {
		return null;
	}

	if ( ! isset( $selector['level'], $selector['occurrence'] ) ) {
		return null;
	}

	$level      = (int) $selector['level'];
	$occurrence = max( 1, (int) $selector['occurrence'] );
	$current    = 0;
	$target_idx = null;

	foreach ( $blocks as $idx => $block ) {
		$name = isset( $block['blockName'] ) ? $block['blockName'] : '';
		if ( 'core/heading' === $name ) {
			$attrs_level = isset( $block['attrs']['level'] ) ? (int) $block['attrs']['level'] : 2;
			if ( $attrs_level === $level ) {
				$current++;
				if ( $current === $occurrence ) {
					$target_idx = $idx;
					break;
				}
			}
		}
	}

	if ( null === $target_idx ) {
		return null;
	}

	if ( 'after_heading' === $position ) {
		return $target_idx + 1;
	}

	// Determine section range: from heading to just before next heading of same or higher level.
	$end_idx = count( $blocks );
	for ( $i = $target_idx + 1; $i < count( $blocks ); $i++ ) {
		$name = isset( $blocks[ $i ]['blockName'] ) ? $blocks[ $i ]['blockName'] : '';
		if ( 'core/heading' === $name ) {
			$attrs_level = isset( $blocks[ $i ]['attrs']['level'] ) ? (int) $blocks[ $i ]['attrs']['level'] : 2;
			if ( $attrs_level <= $level ) {
				$end_idx = $i;
				break;
			}
		}
	}

	if ( 'end_of_section' === $position ) {
		return $end_idx;
	}

	// after_first_paragraph_in_section
	$para_target = max( 1, (int) $paragraph_occurrence );
	$para_seen   = 0;
	for ( $i = $target_idx + 1; $i < $end_idx; $i++ ) {
		$name = isset( $blocks[ $i ]['blockName'] ) ? $blocks[ $i ]['blockName'] : '';
		if ( 'core/paragraph' === $name ) {
			$para_seen++;
			if ( $para_seen === $para_target ) {
				return $i + 1;
			}
		}
	}

	// Fallback: after heading.
	return $target_idx + 1;
}

/**
 * AJAX handler that reports inline image processing status for a post.
 *
 * @return void
 */
function saiap_ajax_inline_images_status() {
	if ( ! current_user_can( 'manage_options' ) ) {
		wp_send_json_error(
			array(
				'error' => 'forbidden',
			),
			403
		);
	}

	$nonce_valid = check_ajax_referer( 'saiap_inline_images_status', 'nonce', false );
	if ( false === $nonce_valid ) {
		wp_send_json_error(
			array(
				'error' => 'invalid_nonce',
			),
			403
		);
	}

	$post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0;
	if ( ! $post_id || ! get_post( $post_id ) ) {
		wp_send_json_error(
			array(
				'error' => 'invalid_post',
			),
			400
		);
	}

	$status = get_post_meta( $post_id, '_saiap_inline_images_status', true );
	if ( empty( $status ) ) {
		$status = 'none';
	}

	$inserted_ids = get_post_meta( $post_id, '_saiap_inline_images_inserted_ids', true );
	if ( ! is_array( $inserted_ids ) ) {
		$inserted_ids = array();
	}

	$sanitized_ids  = array_filter( array_map( 'absint', $inserted_ids ) );
	$inserted_count = count( $sanitized_ids );
	$last_error     = (string) get_post_meta( $post_id, '_saiap_inline_images_last_error', true );

	wp_send_json_success(
		array(
			'status'        => $status,
			'insertedCount' => $inserted_count,
			'lastError'     => $last_error,
		)
	);
}
add_action( 'wp_ajax_saiap_inline_images_status', 'saiap_ajax_inline_images_status' );

// =============================================================================
// Image Agent Trigger (Agentic Image Pipeline)
// =============================================================================

/**
 * Trigger the image agent to add images to a post.
 *
 * This is the entry point for the agentic image pipeline. It calls the premium
 * backend's image agent endpoint which continues the LLM session that generated
 * the content and uses tools to search, select, and insert images.
 *
 * @param int    $post_id    Post ID to add images to.
 * @param string $session_id Session ID from content generation.
 * @return void
 */
function saiap_trigger_image_agent( $post_id, $session_id ) {
	$post_id    = absint( $post_id );
	$session_id = sanitize_text_field( $session_id );

	if ( ! $post_id || ! $session_id ) {
		error_log( '[ClearPost] Image agent trigger skipped: missing post_id or session_id' );
		return;
	}

	// Generate HMAC credentials for WordPress REST endpoints
	$hmac = saiap_generate_image_agent_hmac( $post_id );

	// Build WordPress REST API URL for image agent endpoints
	$wp_rest_url = rest_url( 'simple-ai-auto-post/v1' );

	// Get domain and license key for premium access validation
	$domain      = function_exists( 'saiap_get_server_host' ) ? saiap_get_server_host() : wp_parse_url( home_url(), PHP_URL_HOST );
	$license_key = get_option( 'saiap_license_key', '' );

	// Build request to premium backend
	$url  = SAIAP_PREMIUM_API_URL . '/api/images/agent';
	$body = array(
		'session_id'  => $session_id,
		'post_id'     => $post_id,
		'wp_rest_url' => $wp_rest_url,
		'timestamp'   => $hmac['timestamp'],
		'token'       => $hmac['token'],
		'domain'      => $domain,
		'license_key' => $license_key,
	);

	error_log( sprintf(
		'[ClearPost] Triggering image agent: post_id=%d, session_id=%s, url=%s',
		$post_id,
		substr( $session_id, 0, 8 ) . '...',
		$url
	) );

	// Fire-and-forget request (non-blocking)
	$args = array(
		'blocking' => false,
		'timeout'  => 1,
		'headers'  => array(
			'Content-Type' => 'application/json',
		),
		'body'     => wp_json_encode( $body ),
	);

	$response = wp_remote_post( $url, $args );

	if ( is_wp_error( $response ) ) {
		// Log error but don't block - this is async
		error_log( '[ClearPost] Image agent trigger failed: ' . $response->get_error_message() );
		update_post_meta( $post_id, '_saiap_image_agent_status', 'error' );
		update_post_meta( $post_id, '_saiap_image_agent_error', $response->get_error_message() );
	} else {
		error_log( '[ClearPost] Image agent trigger sent successfully' );
	}
}

/**
 * Trigger the image agent without a stored session (for pull-path delivered posts).
 * Sends post content directly so the backend can build a synthetic session.
 *
 * @param int    $post_id      Post ID to add images to.
 * @param string $post_title   Post title.
 * @param string $post_content Post content (HTML).
 * @return void
 */
function saiap_trigger_image_agent_sessionless( $post_id, $post_title, $post_content ) {
	$post_id = absint( $post_id );
	if ( ! $post_id || empty( $post_content ) ) {
		return;
	}

	$hmac        = saiap_generate_image_agent_hmac( $post_id );
	$wp_rest_url = rest_url( 'simple-ai-auto-post/v1' );
	$domain      = function_exists( 'saiap_get_server_host' ) ? saiap_get_server_host() : wp_parse_url( home_url(), PHP_URL_HOST );
	$license_key = get_option( 'saiap_license_key', '' );

	$url  = SAIAP_PREMIUM_API_URL . '/api/images/agent';
	$body = array(
		'post_id'      => $post_id,
		'post_title'   => $post_title,
		'post_content' => $post_content,
		'wp_rest_url'  => $wp_rest_url,
		'timestamp'    => $hmac['timestamp'],
		'token'        => $hmac['token'],
		'domain'       => $domain,
		'license_key'  => $license_key,
	);

	error_log( sprintf(
		'[ClearPost] Triggering sessionless image agent: post_id=%d, url=%s',
		$post_id,
		$url
	) );

	$response = wp_remote_post( $url, array(
		'blocking' => false,
		'timeout'  => 1,
		'headers'  => array( 'Content-Type' => 'application/json' ),
		'body'     => wp_json_encode( $body ),
	) );

	if ( is_wp_error( $response ) ) {
		error_log( '[ClearPost] Sessionless image agent trigger failed: ' . $response->get_error_message() );
	}
}

/**
 * Get secret used for inline images HMAC.
 *
 * @return string
 */
function saiap_inline_images_hmac_secret() {
	// Tie to site-specific salts.
	return wp_salt( 'saiap_inline_images' );
}

// =============================================================================
// Image Agent HMAC Authentication (30-minute validity)
// =============================================================================

/**
 * Handle GET /posts/{id}/image-agent-credentials
 *
 * Returns fresh HMAC credentials for an existing post so the backend can run
 * image agent on demand without a content-generation session. Used by the
 * admin endpoint for retroactive image insertion.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response|WP_Error
 */
function saiap_handle_get_image_agent_credentials( WP_REST_Request $request ) {
	$post_id = absint( $request->get_param( 'id' ) );
	$post    = get_post( $post_id );

	if ( ! $post ) {
		return new WP_Error( 'post_not_found', 'Post not found', array( 'status' => 404 ) );
	}

	$hmac = saiap_generate_image_agent_hmac( $post_id );

	return rest_ensure_response(
		array(
			'post_id'    => $post_id,
			'wp_rest_url' => rest_url( 'simple-ai-auto-post/v1' ),
			'timestamp'  => $hmac['timestamp'],
			'token'      => $hmac['token'],
		)
	);
}

/**
 * Get secret used for image agent HMAC.
 *
 * @return string
 */
function saiap_image_agent_hmac_secret() {
	return wp_salt( 'saiap_image_agent' );
}

/**
 * Validate HMAC token for image agent endpoints.
 * Uses 30-minute validity window (longer than inline images 5-minute window).
 *
 * @param int    $post_id   Post ID included in HMAC.
 * @param int    $timestamp Unix timestamp from request.
 * @param string $token     HMAC token from request.
 * @return bool|WP_Error True if valid, WP_Error otherwise.
 */
function saiap_validate_image_agent_hmac( $post_id, $timestamp, $token ) {
	$now = time();

	// 30-minute replay window.
	if ( ! $timestamp || abs( $now - $timestamp ) > 30 * MINUTE_IN_SECONDS ) {
		return new WP_Error( 'invalid_timestamp', __( 'Token expired or invalid timestamp', 'clearpost-simple-ai-auto-post' ) );
	}

	$secret   = saiap_image_agent_hmac_secret();
	$expected = hash_hmac( 'sha256', $post_id . '|' . $timestamp, $secret );
	$is_valid = hash_equals( $expected, $token );

	if ( ! $is_valid ) {
		return new WP_Error( 'invalid_token', __( 'Invalid authentication token', 'clearpost-simple-ai-auto-post' ) );
	}

	return true;
}

/**
 * Generate HMAC token for image agent endpoints.
 *
 * @param int $post_id Post ID.
 * @return array { timestamp: int, token: string }
 */
function saiap_generate_image_agent_hmac( $post_id ) {
	$timestamp = time();
	$secret    = saiap_image_agent_hmac_secret();
	$token     = hash_hmac( 'sha256', $post_id . '|' . $timestamp, $secret );

	return array(
		'timestamp' => $timestamp,
		'token'     => $token,
	);
}

// =============================================================================
// Image Agent REST Handlers
// =============================================================================

/**
 * Handle GET /posts/{id}/content - Return post block structure with heading coordinates.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response
 */
function saiap_handle_get_post_content( WP_REST_Request $request ) {
	$post_id   = absint( $request->get_param( 'id' ) );
	$summary   = (bool) $request->get_param( 'summary' );
	$timestamp = (int) $request->get_param( 'timestamp' );
	$token     = (string) $request->get_param( 'token' );

	error_log( sprintf( '[ClearPost] get_post_content called: post_id=%d, summary=%s', $post_id, $summary ? 'true' : 'false' ) );

	// Validate HMAC.
	$auth_result = saiap_validate_image_agent_hmac( $post_id, $timestamp, $token );
	if ( is_wp_error( $auth_result ) ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => $auth_result->get_error_code(),
				'message' => $auth_result->get_error_message(),
			),
			403
		);
	}

	$post = get_post( $post_id );
	if ( ! $post ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => 'not_found',
				'message' => 'Post not found',
			),
			404
		);
	}

	$content = $post->post_content;
	$blocks  = parse_blocks( $content );

	// Get feature image info.
	$feature_image    = null;
	$thumbnail_id     = get_post_thumbnail_id( $post_id );
	if ( $thumbnail_id ) {
		$feature_image = array(
			'attachment_id' => (int) $thumbnail_id,
			'url'           => wp_get_attachment_url( $thumbnail_id ),
			'alt'           => get_post_meta( $thumbnail_id, '_wp_attachment_image_alt', true ),
		);
	}

	// If summary mode, return minimal info.
	if ( $summary ) {
		$inline_images = saiap_extract_inline_images( $blocks );
		return new WP_REST_Response(
			array(
				'success'        => true,
				'title'          => $post->post_title,
				'feature_image'  => $feature_image,
				'inline_images'  => $inline_images,
				'block_count'    => count( $blocks ),
				'word_count'     => str_word_count( wp_strip_all_tags( $content ) ),
			),
			200
		);
	}

	// Full mode: return block structure with heading coordinates.
	$block_structure = saiap_extract_block_structure( $blocks );
	$inline_images   = saiap_extract_inline_images( $blocks );

	return new WP_REST_Response(
		array(
			'success'       => true,
			'title'         => $post->post_title,
			'feature_image' => $feature_image,
			'blocks'        => $block_structure,
			'inline_images' => $inline_images,
			'block_count'   => count( $blocks ),
			'word_count'    => str_word_count( wp_strip_all_tags( $content ) ),
		),
		200
	);
}

/**
 * Extract block structure with heading coordinates for AI consumption.
 *
 * @param array $blocks Parsed blocks.
 * @return array
 */
function saiap_extract_block_structure( $blocks ) {
	$structure       = array();
	$heading_counts  = array(); // Track occurrence count per level.

	foreach ( $blocks as $idx => $block ) {
		$name = isset( $block['blockName'] ) ? $block['blockName'] : null;

		if ( null === $name ) {
			continue; // Skip empty/whitespace blocks.
		}

		$item = array(
			'index' => $idx,
			'type'  => $name,
		);

		if ( 'core/heading' === $name ) {
			$level = isset( $block['attrs']['level'] ) ? (int) $block['attrs']['level'] : 2;

			if ( ! isset( $heading_counts[ $level ] ) ) {
				$heading_counts[ $level ] = 0;
			}
			$heading_counts[ $level ]++;

			$item['heading_level']      = $level;
			$item['heading_occurrence'] = $heading_counts[ $level ];
			$item['text']               = wp_strip_all_tags( $block['innerHTML'] );
		} elseif ( 'core/paragraph' === $name ) {
			$item['text'] = wp_strip_all_tags( substr( $block['innerHTML'], 0, 200 ) );
			if ( strlen( $block['innerHTML'] ) > 200 ) {
				$item['text'] .= '...';
			}
		} elseif ( 'core/image' === $name ) {
			$item['attachment_id'] = isset( $block['attrs']['id'] ) ? (int) $block['attrs']['id'] : null;
			$item['alt']           = isset( $block['attrs']['alt'] ) ? $block['attrs']['alt'] : '';
		}

		$structure[] = $item;
	}

	return $structure;
}

/**
 * Extract inline images from blocks.
 *
 * @param array $blocks Parsed blocks.
 * @return array
 */
function saiap_extract_inline_images( $blocks ) {
	$images = array();

	foreach ( $blocks as $idx => $block ) {
		$name = isset( $block['blockName'] ) ? $block['blockName'] : null;

		if ( 'core/image' === $name ) {
			$attachment_id = isset( $block['attrs']['id'] ) ? (int) $block['attrs']['id'] : null;
			$images[]      = array(
				'block_index'   => $idx,
				'attachment_id' => $attachment_id,
				'url'           => $attachment_id ? wp_get_attachment_url( $attachment_id ) : null,
				'alt'           => isset( $block['attrs']['alt'] ) ? $block['attrs']['alt'] : '',
			);
		}
	}

	return $images;
}

/**
 * Handle POST /posts/{id}/feature-image - Set feature image for post.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response
 */
function saiap_handle_set_feature_image( WP_REST_Request $request ) {
	$post_id   = absint( $request->get_param( 'id' ) );
	$image_url = esc_url_raw( $request->get_param( 'image_url' ) );
	$alt_text  = sanitize_text_field( $request->get_param( 'alt_text' ) );
	$timestamp = (int) $request->get_param( 'timestamp' );
	$token     = (string) $request->get_param( 'token' );

	error_log( sprintf( '[ClearPost] set_feature_image called: post_id=%d, url=%s', $post_id, substr( $image_url, 0, 50 ) . '...' ) );

	// Validate HMAC.
	$auth_result = saiap_validate_image_agent_hmac( $post_id, $timestamp, $token );
	if ( is_wp_error( $auth_result ) ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => $auth_result->get_error_code(),
				'message' => $auth_result->get_error_message(),
			),
			403
		);
	}

	$post = get_post( $post_id );
	if ( ! $post ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => 'not_found',
				'message' => 'Post not found',
			),
			404
		);
	}

	if ( empty( $image_url ) ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => 'invalid_url',
				'message' => 'Image URL is required',
			),
			400
		);
	}

	// Use existing sideload function.
	$result = saiap_set_featured_image_from_url( $post_id, $image_url, $alt_text );

	if ( is_wp_error( $result ) ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => $result->get_error_code(),
				'message' => $result->get_error_message(),
			),
			500
		);
	}

	$attachment_url = wp_get_attachment_url( $result );

	return new WP_REST_Response(
		array(
			'success'        => true,
			'attachment_id'  => (int) $result,
			'attachment_url' => $attachment_url,
		),
		200
	);
}

/**
 * Handle POST /posts/{id}/inline-image - Insert inline image at selector position.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response
 */
function saiap_handle_insert_inline_image( WP_REST_Request $request ) {
	$post_id       = absint( $request->get_param( 'id' ) );
	$image_url     = esc_url_raw( $request->get_param( 'image_url' ) );
	$after_heading = $request->get_param( 'after_heading' );
	$position      = sanitize_text_field( $request->get_param( 'position' ) );
	$alt_text      = sanitize_text_field( $request->get_param( 'alt_text' ) );
	$caption       = sanitize_text_field( $request->get_param( 'caption' ) );
	$timestamp     = (int) $request->get_param( 'timestamp' );
	$token         = (string) $request->get_param( 'token' );

	$selector_str = is_array( $after_heading )
		? sprintf( 'H%d occ %d', isset( $after_heading['level'] ) ? $after_heading['level'] : 0, isset( $after_heading['occurrence'] ) ? $after_heading['occurrence'] : 0 )
		: 'invalid';
	error_log( sprintf( '[ClearPost] insert_inline_image called: post_id=%d, selector=%s, position=%s', $post_id, $selector_str, $position ) );

	// Validate HMAC.
	$auth_result = saiap_validate_image_agent_hmac( $post_id, $timestamp, $token );
	if ( is_wp_error( $auth_result ) ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => $auth_result->get_error_code(),
				'message' => $auth_result->get_error_message(),
			),
			403
		);
	}

	$post = get_post( $post_id );
	if ( ! $post ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => 'not_found',
				'message' => 'Post not found',
			),
			404
		);
	}

	if ( empty( $image_url ) ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => 'invalid_url',
				'message' => 'Image URL is required',
			),
			400
		);
	}

	// Validate selector.
	if ( ! is_array( $after_heading ) || ! isset( $after_heading['level'], $after_heading['occurrence'] ) ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => 'invalid_selector',
				'message' => 'after_heading must have level and occurrence',
			),
			400
		);
	}

	$selector = array(
		'level'      => (int) $after_heading['level'],
		'occurrence' => (int) $after_heading['occurrence'],
	);

	// Map position parameter to internal position format.
	$internal_position = 'after_heading';
	if ( 'after_first_paragraph' === $position ) {
		$internal_position = 'after_first_paragraph_in_section';
	}

	// Sideload image and create attachment.
	$attach_id = saiap_create_image_attachment_from_url( $post_id, $image_url, $alt_text );
	if ( is_wp_error( $attach_id ) ) {
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => $attach_id->get_error_code(),
				'message' => $attach_id->get_error_message(),
			),
			500
		);
	}

	// Build image block HTML.
	$block_html = saiap_build_core_image_block_html( $attach_id, $alt_text, 'large' );
	if ( '' === $block_html ) {
		wp_delete_attachment( $attach_id, true );
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => 'block_build_failed',
				'message' => 'Failed to build image block',
			),
			500
		);
	}

	// Insert block at selector position.
	$current_content = $post->post_content;
	$new_content     = saiap_insert_block_by_selector( $current_content, $selector, $internal_position, $block_html, 1 );

	if ( is_wp_error( $new_content ) ) {
		wp_delete_attachment( $attach_id, true );
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => $new_content->get_error_code(),
				'message' => $new_content->get_error_message(),
			),
			500
		);
	}

	// Check if content was actually modified.
	if ( $new_content === $current_content ) {
		wp_delete_attachment( $attach_id, true );
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => 'selector_not_found',
				'message' => sprintf(
					'Heading level %d occurrence %d not found in post content',
					$selector['level'],
					$selector['occurrence']
				),
			),
			400
		);
	}

	// Update post content.
	$update_result = wp_update_post(
		array(
			'ID'           => $post_id,
			'post_content' => $new_content,
		),
		true
	);

	if ( is_wp_error( $update_result ) ) {
		wp_delete_attachment( $attach_id, true );
		return new WP_REST_Response(
			array(
				'success' => false,
				'error'   => 'update_failed',
				'message' => $update_result->get_error_message(),
			),
			500
		);
	}

	$attachment_url = wp_get_attachment_url( $attach_id );

	return new WP_REST_Response(
		array(
			'success'        => true,
			'attachment_id'  => (int) $attach_id,
			'attachment_url' => $attachment_url,
			'selector'       => $selector,
			'position'       => $position,
		),
		200
	);
}


