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

use SabaiApps\Directories\Component\Form\Form;

/**
 * EditorField provides a code/text editor field using CodeMirror.
 * Supports syntax highlighting for CSS, JavaScript, and XML, and file saving.
 */
class EditorField extends AbstractField {

	// Static property to store editor elements per form.
	protected static $_elements = array();

	/**
	 * Initializes the Editor field and registers the pre-render callback.
	 *
	 * @param string $name
	 * @param array  $data
	 * @param Form   $form
	 */
	public function formFieldInit( $name, array &$data, Form $form ) {
		if ( ! isset( $data['#id'] ) ) {
			$data['#id'] = $form->getFieldId( $name );
		}
		if ( ! isset( $data['#language'] )
			|| ! in_array( $data['#language'], array( 'css', 'javascript', 'xml' ) )
		) {
			$data['#language'] = 'xml';
		}
		if ( ! isset( self::$_elements[ $form->settings['#id'] ] ) ) {
			self::$_elements[ $form->settings['#id'] ] = array();
		}
		self::$_elements[ $form->settings['#id'] ][ $data['#id'] ] = $data['#language'];

		// Register pre-render callback for CodeMirror integration
		$form->settings['#pre_render'][ __CLASS__ ] = array( $this, 'preRenderCallback' );
	}

	/**
	 * Handles value processing, validation, and file saving for the Editor field on submit.
	 *
	 * @param mixed $value
	 * @param array $data
	 * @param Form  $form
	 */
	public function formFieldSubmit( &$value, array &$data, Form $form ) {
		if ( false !== $validated = $this->_application->Form_Validate_text( $value, $data, $form, true, true ) ) {
			$value = $validated;
		}
		if ( isset( $data['#file_path'] ) ) {
			if ( strlen( $value ) ) {
				if ( false === file_put_contents( $data['#file_path'], $value ) ) {
					$form->setError( 'Error writing into file: ' . $data['#file_path'] . '. Make sure the file can be created and is writeable.' );
				} else {
					$value = null;
				}
			} else {
				@unlink( $data['#file_path'] );
			}
		}
	}

	/**
	 * Renders the Editor field as a textarea for CodeMirror.
	 *
	 * @param array $data
	 * @param Form  $form
	 */
	public function formFieldRender( array &$data, Form $form ) {
		if ( isset( $data['#default_value'] ) ) {
			$content = $data['#default_value'];
		} elseif ( isset( $data['#file_path'] ) ) {
			if ( file_exists( $data['#file_path'] ) ) {
				$content = @file_get_contents( $data['#file_path'] );
			}
		}
		$html = sprintf(
			'<textarea id="%s-editor" name="%s"%s>%s</textarea>',
			$data['#id'],
			$data['#name'],
			$this->_application->Attr( $data['#attributes'] ),
			isset( $content ) && strlen( $content ) ? $this->_application->H( $content ) : ''
		);
		$this->_render( $html, $data, $form );
	}

	/**
	 * Pre-render callback to load CodeMirror scripts and initialize editors.
	 *
	 * @param Form $form
	 */
	public function preRenderCallback( Form $form ) {
		// Add CodeMirror scripts and styles
		$this->_application->getPlatform()
			->addJsFile( 'codemirror.min.js', 'codemirror', null, null, true, true )
			->addCssFile( 'codemirror.min.css', 'codemirror', null, null, null, true )
			->addCssFile( 'codemirror/theme/mdn-like.min.css', 'codemirror-theme-midnight', array( 'codemirror' ), null, null, true );
		// Add JS to instantiate CodeMirror editors
		$js    = array( 'var codeMirrors = [];' );
		$langs = array();
		foreach ( self::$_elements[ $form->settings['#id'] ] as $id => $lang ) {
			$langs[ $lang ] = $lang;
			$js[]           = 'codeMirrors.push(CodeMirror.fromTextArea(document.getElementById("' . $id . '-editor"), {
        lineNumbers: true,
        mode: "' . $lang . '",
        theme: "mdn-like"
    }));';
		}
		// Ensure CodeMirror content is saved before AJAX form submission
		$js[] = sprintf(
			'$("#%s").on("form_ajax_submit.sabai", function (e) {
    codeMirrors.forEach(function (cm) {
        cm.save();
    });
});',
			$form->settings['#id']
		);
		// Refresh CodeMirror instance when switching tabs
		$js[] = '$("a[data-bs-toggle=\'pill\']").on("shown.bs.tab", function (e) {
    var cmContainer = $(e.target.getAttribute("data-bs-target")).find(".CodeMirror")[0];
    if (cmContainer && cmContainer.CodeMirror && !cmContainer.codeMirrorRefreshed) {
        cmContainer.CodeMirror.refresh();
        cmContainer.codeMirrorRefreshed = true;
    }
});';
		// Add CodeMirror modes for each language used
		foreach ( $langs as $lang ) {
			$this->_application->getPlatform()
				->addJsFile( 'codemirror/mode/' . $lang . '.min.js', 'codemirror-mode-' . $lang, array( 'codemirror' ), null, true, true );
		}
		// Add the generated JavaScript to the form's ready scripts
		$form->settings['#js_ready'][] = implode( PHP_EOL, $js );
	}
}
