<?php

namespace GravityKit\GravityExport\PdfRenderer\Rendering;

use GFFormsModel;
use GravityKit\GravityExport\Feature;

/**
 * Feature that inlines images inside a PDF render.
 * @since $ver$
 */
final class InlineImages extends Feature {
	/**
	 * The setting name to inline images.
	 * @since $ver$
	 */
	private const SETTING_INLINE_IMAGES = 'pdf-inline-images';

	/**
	 * Holds a list of urls that should be inline images.
	 * @since $ver$
	 * @var array
	 */
	private $marks = [];

	/**
	 * @inheritDoc
	 * @since
	 */
	protected function init(): void {
		add_filter( 'gk/gravityexport/pdf/page/sheet', \Closure::fromCallable( [ $this, 'replace_image_tags' ] ) );
		add_filter( 'gk/gravityexport/settings/sections', \Closure::fromCallable( [ $this, 'add_settings' ] ), 10, 2 );
		add_filter( 'gfexcel_field_value_fileupload', \Closure::fromCallable( [ $this, 'mark_images' ] ), 10, 3 );
	}

	/**
	 * Marks `fileupload` fields values as images if the value is an image.
	 *
	 * @param string|null $value The value.
	 * @param array       $entry The entry.
	 * @param \GF_Field   $field The field.
	 *
	 * @return string The updated value.
	 */
	private function mark_images( ?string $value, array $entry, \GF_Field $field ): string {
		if (
			$this->should_inline_image( $field->formId ?? 0, $field->id ?? 0 )
			&& $this->is_image( $entry, (int) ( $field->id ?? 0 ) )
		) {
			$this->marks[ (string) $value ] = $entry[ $field->id ?? 0 ];
		}


		// Replacing the value with the real image path; since we are displaying it.
		return (string) $value;
	}

	/**
	 * Whether the provided field is an image.
	 * @since $ver$
	 */
	private function is_image( array $entry, int $field_id ): bool {
		$path = GFFormsModel::get_physical_file_path( $entry[ $field_id ] ?? '', $entry['id'] ?? 0 );
		if ( function_exists( 'mime_content_type' ) && trim( (string) $path ) !== '' ) {
			$content_type = @mime_content_type( $path );
			if ( $content_type !== false ) {
				return strpos( $content_type, 'image' ) !== false;
			}
		}

		return preg_match( '/\.(png|jpe?g|gif|webp)$/i', $path );
	}

	/**
	 * Add the settings to the foundation settings page.
	 * @since $ver$
	 *
	 * @param array $sections The sections.
	 *
	 * @return array The updated sections.
	 */
	private function add_settings( array $sections ): array {
		foreach ( $sections as $s => $section ) {
			if ( 'pdf-section' !== ( $section['id'] ?? null ) ) {
				continue;
			}

			$sections[ $s ]['settings'][] = [
				'name'        => self::SETTING_INLINE_IMAGES,
				'id'          => self::SETTING_INLINE_IMAGES,
				'title'       => esc_html__( 'Inline images', 'gk-gravityexport' ),
				'description' => esc_html__( 'File uploads will be rendered inside the PDF if the upload is an image.', 'gk-gravityexport' ),
				'type'        => 'checkbox',
				'value'       => $this->addon->get_plugin_setting( self::SETTING_INLINE_IMAGES ) ?? false,
			];
		}

		return $sections;
	}

	/**
	 * Whether the images should be inlined.
	 * @since $ver$
	 *
	 * @param int $form_id  The form ID.
	 * @param int $field_id The field ID.
	 *
	 * @return bool Whether to inline the image.
	 */
	private function should_inline_image( int $form_id, int $field_id ): bool {
		$value = $this->addon->get_plugin_setting( self::SETTING_INLINE_IMAGES ) ?? false;

		return (bool) gf_apply_filters( [
			'gk/gravityexport/pdf/inline-images',
			$form_id,
			$field_id,
		],
			$value,
			$form_id,
			$field_id
		);
	}

	/**
	 * Replaces marked values with a real image tag.
	 * @since $ver$
	 *
	 * @param string $html The PDF table HTML.
	 *
	 * @return string The updated HTML.
	 */
	private function replace_image_tags( string $html ): string {
		return preg_replace_callback( '/<a[^>]+>(?<url>.*?)<\/a>/is', function ( array $matches ) {
			$url = htmlspecialchars_decode( $matches['url'] );
			if ( isset( $this->marks[ $url ] ) ) {
				return sprintf( '<img src="%s" class="inline-image"/>', $this->marks[ $url ] );
			}

			return $matches[0];

		}, $html );
	}
}
