<?php
/**
 * SocialPromoter Image Handler
 *
 * Fetches images from the WordPress media library, validates them,
 * and prepares them for upload to social media platforms.
 *
 * @package PublisherKits_SocialPromoter
 */

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

/**
 * Class PKITS_SP_Image_Handler
 */
class PKITS_SP_Image_Handler {

	/**
	 * Maximum file size in bytes (5MB - safe limit for all platforms).
	 *
	 * @var int
	 */
	const MAX_FILE_SIZE = 5242880;

	/**
	 * Allowed MIME types.
	 *
	 * @var string[]
	 */
	const ALLOWED_TYPES = array( 'image/jpeg', 'image/png', 'image/gif', 'image/webp' );

	/**
	 * Get the featured image URL for a post.
	 *
	 * Returns the full-size URL of the post's featured image, or empty
	 * string if none exists.
	 *
	 * @param int $post_id WordPress post ID.
	 * @return string Image URL or empty string.
	 */
	public static function get_featured_image_url( $post_id ) {
		$thumbnail_id = get_post_thumbnail_id( absint( $post_id ) );
		if ( ! $thumbnail_id ) {
			return '';
		}

		$image = wp_get_attachment_image_src( $thumbnail_id, 'full' );
		if ( empty( $image[0] ) ) {
			return '';
		}

		return $image[0];
	}

	/**
	 * Get featured image data for a post.
	 *
	 * Returns an array with url, width, height, mime_type, and local_path.
	 *
	 * @param int $post_id WordPress post ID.
	 * @return array|null Image data or null if no featured image.
	 */
	public static function get_featured_image_data( $post_id ) {
		$thumbnail_id = get_post_thumbnail_id( absint( $post_id ) );
		if ( ! $thumbnail_id ) {
			return null;
		}

		return self::get_attachment_data( $thumbnail_id );
	}

	/**
	 * Get image data for a media library attachment ID.
	 *
	 * @param int $attachment_id WordPress attachment ID.
	 * @return array|null Image data or null if invalid.
	 */
	public static function get_attachment_data( $attachment_id ) {
		$attachment_id = absint( $attachment_id );
		if ( ! $attachment_id ) {
			return null;
		}

		$mime_type = get_post_mime_type( $attachment_id );
		if ( ! in_array( $mime_type, self::ALLOWED_TYPES, true ) ) {
			return null;
		}

		$image = wp_get_attachment_image_src( $attachment_id, 'full' );
		if ( empty( $image[0] ) ) {
			return null;
		}

		// Get local file path.
		$local_path = get_attached_file( $attachment_id );

		// Validate file exists and is within size limit.
		if ( ! $local_path || ! file_exists( $local_path ) ) {
			return null;
		}

		$file_size = filesize( $local_path );
		if ( $file_size > self::MAX_FILE_SIZE ) {
			return null;
		}

		return array(
			'attachment_id' => $attachment_id,
			'url'           => $image[0],
			'width'         => isset( $image[1] ) ? (int) $image[1] : 0,
			'height'        => isset( $image[2] ) ? (int) $image[2] : 0,
			'mime_type'     => $mime_type,
			'local_path'    => $local_path,
			'file_size'     => $file_size,
		);
	}

	/**
	 * Get image data from a URL (for externally hosted images).
	 *
	 * Downloads the image to a temporary file for platform upload.
	 * Caller is responsible for cleaning up the temp file.
	 *
	 * @param string $url Image URL.
	 * @return array|WP_Error Image data array or WP_Error.
	 */
	public static function get_image_data_from_url( $url ) {
		// Use the SAFE HTTP API: it rejects loopback/private/link-local hosts, so
		// a caller-supplied URL cannot be used to probe internal services or the
		// cloud metadata endpoint (SSRF). Own-site media never reaches here — it
		// is resolved to a local attachment before this call. limit_response_size
		// caps the download so an oversized/hostile URL cannot exhaust memory
		// before the size check below.
		$response = wp_safe_remote_get(
			$url,
			array(
				'timeout'             => 30,
				'sslverify'           => true,
				'limit_response_size' => self::MAX_FILE_SIZE + 1,
			)
		);

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$code = wp_remote_retrieve_response_code( $response );
		if ( 200 !== (int) $code ) {
			return new WP_Error( 'image_fetch_failed', 'Could not fetch image: HTTP ' . $code );
		}

		$content_type = wp_remote_retrieve_header( $response, 'content-type' );
		// A repeated header comes back as an array; normalise to a string before
		// strtok() (which would TypeError on an array under PHP 8).
		if ( is_array( $content_type ) ) {
			$content_type = reset( $content_type );
		}
		$mime_type    = strtok( (string) $content_type, ';' );

		if ( ! in_array( trim( $mime_type ), self::ALLOWED_TYPES, true ) ) {
			return new WP_Error( 'image_type_invalid', 'Unsupported image type: ' . esc_html( $mime_type ) );
		}

		$body = wp_remote_retrieve_body( $response );
		if ( strlen( $body ) > self::MAX_FILE_SIZE ) {
			return new WP_Error( 'image_too_large', 'Image exceeds 5MB size limit.' );
		}

		// Write to temp file.
		$ext      = self::mime_to_extension( trim( $mime_type ) );
		$tmp_file = wp_tempnam( 'pkits_sp_img_' . time() . '.' . $ext );
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
		if ( false === file_put_contents( $tmp_file, $body ) ) {
			return new WP_Error( 'image_write_failed', 'Could not write image to temporary file.' );
		}

		return array(
			'attachment_id' => 0,
			'url'           => $url,
			'width'         => 0,
			'height'        => 0,
			'mime_type'     => trim( $mime_type ),
			'local_path'    => $tmp_file,
			'file_size'     => strlen( $body ),
			'temp_file'     => true,
		);
	}

	/**
	 * Convert MIME type to file extension.
	 *
	 * @param string $mime_type MIME type string.
	 * @return string File extension without dot.
	 */
	public static function mime_to_extension( $mime_type ) {
		$map = array(
			'image/jpeg' => 'jpg',
			'image/png'  => 'png',
			'image/gif'  => 'gif',
			'image/webp' => 'webp',
		);
		return isset( $map[ $mime_type ] ) ? $map[ $mime_type ] : 'jpg';
	}

	/**
	 * Clean up a temporary image file.
	 *
	 * @param array $image_data Image data array.
	 * @return void
	 */
	public static function cleanup_temp( array $image_data ) {
		if ( ! empty( $image_data['temp_file'] ) && ! empty( $image_data['local_path'] ) ) {
			if ( file_exists( $image_data['local_path'] ) ) {
				wp_delete_file( $image_data['local_path'] );
			}
		}
	}

	/**
	 * Human-readable one-line descriptor of the image actually on disk, for
	 * error logs. Reads the real file (not the stored width/height, which for a
	 * temp image are the ORIGINAL source dimensions, not the rendered
	 * canvas) so a connector can report exactly what it tried to upload — the
	 * information needed to spot a platform size/format rejection (for example
	 * BlueSky's ~976KB blob limit).
	 *
	 * @param array $image_data Image data array (expects local_path).
	 * @return string e.g. "312 KB, 1200x627px, image/jpeg".
	 */
	public static function describe_for_log( array $image_data ) {
		$path = $image_data['local_path'] ?? '';
		if ( '' === $path || ! file_exists( $path ) ) {
			return 'image missing on disk';
		}

		$parts = array( size_format( (int) filesize( $path ) ) ?: '0 B' );

		$info = @getimagesize( $path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
		if ( is_array( $info ) ) {
			$parts[] = (int) $info[0] . 'x' . (int) $info[1] . 'px';
		}

		$mime = ! empty( $image_data['mime_type'] )
			? $image_data['mime_type']
			: ( is_array( $info ) && ! empty( $info['mime'] ) ? $info['mime'] : 'unknown type' );
		$parts[] = $mime;

		return implode( ', ', $parts );
	}
}
