<?php
namespace SabaiApps\Directories\Component\Form\Field;

use SabaiApps\Directories\Component\Form\Form;
use SabaiApps\Directories\Component\Form\FormComponent;
use SabaiApps\Directories\Component\Form\Helper\UploadHelper;

/**
 * UppyField provides a file upload field using the Uppy JavaScript library.
 * Supports multiple files, file type/size restrictions, and image validation.
 */
class UppyField extends AbstractField {

	/**
	 * Initializes the Uppy field and registers the pre-render callback.
	 *
	 * @param string $name
	 * @param array  $data
	 * @param Form   $form
	 */
	public function formFieldInit( $name, array &$data, Form $form ) {
		$form->settings['#pre_render'][ __CLASS__ ] = array( array( $this, 'preRenderCallback' ), array( $data ) );
	}

	/**
	 * Renders the Uppy field as a dashboard container with data attributes for configuration.
	 *
	 * @param array $data
	 * @param Form  $form
	 */
	public function formFieldRender( array &$data, Form $form ) {
		$field_name = $data['#name'];
		if ( ! empty( $data['#multiple'] ) ) {
			$data['#name'] .= '[]';
		}
		if ( ! isset( $data['#id'] ) ) {
			$data['#id'] = $form->getFieldId( $data['#name'] );
		}
		$allow_only_images = ! empty( $data['#allow_only_images'] );
		if ( empty( $data['#allowed_extensions'] ) ) {
			if ( $allow_only_images ) {
				$allowed_extensions = array( 'jpeg', 'jpg', 'gif', 'png' );
			} else {
				$allowed_extensions = array( 'jpeg', 'jpg', 'gif', 'png', 'txt', 'pdf', 'zip' );
			}
		} else {
			$allowed_extensions = $data['#allowed_extensions'];
		}
		$file_type_ext_map                               = $this->_getFileTypeExtensionMap( $allowed_extensions );
		$file_types                                      = array_keys( $file_type_ext_map );
		$data['#attributes']['data-accepted-file-types'] = implode( ',', $file_types );
		if ( ! empty( $data['#max_num_files'] ) ) {
			$data['#attributes']['data-max-num-files'] = $data['#max_num_files'];
		}
		if ( $max_file_size = $this->_application->System_Util_strToBytes( ! empty( $data['#max_file_size'] ) ? $data['#max_file_size'] : ( ( $size = @ini_get( 'upload_max_filesize' ) ) ? $size : '2M' ) ) ) {
			$data['#attributes']['data-max-file-size'] = $max_file_size;
		}
		$build_id                                    = empty( $form->settings['#build_id'] ) ? $form->settings['#id'] : $form->settings['#build_id'];
		$data['#attributes']['data-upload-endpoint'] = $this->_application->Url( '/_drts/form/upload' );
		if ( isset( $data['#dashboard_height'] ) ) {
			$data['#attributes']['data-dashboard-height'] = $data['#dashboard_height'];
		}

		// Register upload token and settings for this field
		$this->_application->Form_Form_useUploadToken(
			$build_id,
			$upload_id = md5( $field_name ),
			array(
				'max_num_files'   => empty( $data['#max_num_files'] ) ? 0 : $data['#max_num_files'],
				'upload_settings' => array(
					'allowed_extensions'          => $allowed_extensions,
					'max_file_size'               => $max_file_size,
					'image_only'                  => $allow_only_images,
					'min_image_width'             => isset( $data['#min_image_width'] ) ? $data['#min_image_width'] : null,
					'min_image_height'            => isset( $data['#min_image_height'] ) ? $data['#min_image_height'] : null,
					'max_image_width'             => isset( $data['#max_image_width'] ) ? $data['#max_image_width'] : null,
					'max_image_height'            => isset( $data['#max_image_height'] ) ? $data['#max_image_height'] : null,
					'max_num_files'               => $data['#max_num_files'],
					'upload_dir'                  => $this->_application->getComponent( 'System' )->getTmpDir(),
					'upload_file_name_prefix'     => 'uppy_',
					'upload_file_name_append_ext' => false,
				),
			)
		);
		$data['#attributes']['data-form-build-id']   = $build_id;
		$data['#attributes']['data-field-upload-id'] = $upload_id;
		if ( ! empty( $data['#default_value'] ) ) {
			$files = array();
			foreach ( (array) $data['#default_value'] as $file_id ) {
				if ( empty( $file_id ) ) {
					continue;
				}
				$files[] = array(
					'id'   => $file_id,
					'name' => $file_id,
				);
			}
			$data['#attributes']['data-files'] = $this->_application->System_Util_jsonEncode( $files );
		}
		$html = sprintf(
			'<div class="drts-form-uppy-dashboard%s"%s></div>',
			isset( $data['#attributes']['class'] ) ? ' ' . $this->_application->H( $data['#attributes']['class'] ) : '',
			$this->_application->Attr( $data['#attributes'], 'class' )
		);
		$this->_render( $html, $data, $form );
	}

	/**
	 * Handles value processing and file saving for the Uppy field on submit.
	 *
	 * @param mixed $value
	 * @param array $data
	 * @param Form  $form
	 */
	public function formFieldSubmit( &$value, array &$data, Form $form ) {
		$new_files = array();
		if ( ! is_array( $value ) ) {
			$value = array( $value );
		}
		foreach ( array_keys( $value ) as $k ) {
			if ( isset( $value[ $k ] )
				&& strpos( $value[ $k ], '_' ) === 0
			) {
				if ( $file = $this->_application->getPlatform()->getCache( substr( $value[ $k ], 1 ), FormComponent::FILE_UPLOAD_CACHE_GROUP ) ) {
					if ( empty( $file['id'] ) ) { // make sure not already saved during previous failed form submission
						$new_files[ $k ] = $file;
					} else {
						$value[ $k ] = $file['id'];
					}
				} else {
					unset( $value[ $k ] );
				}
			}
		}
		if ( ! empty( $new_files ) ) {
			foreach ( array_keys( $new_files ) as $k ) {
				if ( ! $file_id = $this->_saveFile( $new_files[ $k ] ) ) {
					continue;
				}

				$value[ $k ] = $file_id;

				// Save ID to cache so the file info can be fetched when form submission failed.
				$new_files[ $k ]['id'] = $file_id;
				$this->_application->getPlatform()->setCache( $new_files[ $k ], $new_files[ $k ]['_id'], FormComponent::FILE_UPLOAD_CACHE_LIFETIME, FormComponent::FILE_UPLOAD_CACHE_GROUP );
			}
			ksort( $value );
		}
	}

	/**
	 * Cleans up the Uppy field after form submission.
	 *
	 * @param array $data
	 * @param Form  $form
	 */
	public function formFieldCleanup( array &$data, Form $form ) {
		parent::formFieldCleanup( $data, $form ); // TODO: Change the autogenerated stub
	}

	/**
	 * Saves the uploaded file using the platform's upload handler.
	 *
	 * @param array $file
	 * @return mixed
	 */
	protected function _saveFile( array $file ) {
		return $this->_application->getPlatform()->uploadFile( $file['saved_file_path'], $file['name'] );
	}

	/**
	 * Pre-render callback to load Uppy scripts and initialize the dashboard.
	 *
	 * @param Form  $form
	 * @param array $data
	 */
	public function preRenderCallback( Form $form, $data ) {
		$this->_application->Form_Scripts_uppy();
		$form->settings['#js_ready'][] = sprintf(
			'$("#%1$s").find(".drts-form-type-uppy .drts-form-uppy-dashboard").each(function(){
    DRTS.Form.field.uppy($(this));
});',
			$form->settings['#id']
		);
	}

	/**
	 * Returns a map of accepted file types to their extensions.
	 *
	 * @param array $extensions
	 * @return array
	 */
	protected function _getFileTypeExtensionMap( array $extensions ) {
		$ret = array();
		foreach ( $extensions as $extension ) {
			if ( strpos( $extension, '|' ) ) {
				$extension = explode( '|', $extension );
			} else {
				$extension = array( $extension );
			}
			foreach ( $extension as $ext ) {
				foreach ( (array) UploadHelper::getPossibleMimeTypes( $ext ) as $type ) {
					$ret[ $type ] = $ext;
				}
			}
		}
		return $ret;
	}
}
