<?php
/**
 * Contact Form 7 Module.
 *
 * @package DiviSupreme
 */

declare(strict_types=1);

namespace DiviSupreme\Modules;

if ( ! defined( 'ABSPATH' ) ) {
	die( 'Direct access forbidden.' );
}

use ET\Builder\Framework\DependencyManagement\Interfaces\DependencyInterface;
use ET\Builder\Framework\Utility\HTMLUtility;
use ET\Builder\FrontEnd\Module\Style;
use ET\Builder\Packages\Module\Module;
use ET\Builder\Packages\Module\Options\Element\ElementClassnames;
use ET\Builder\Packages\ModuleLibrary\ModuleRegistration;
use ET\Builder\Packages\Module\Layout\Components\StyleCommon\CommonStyle;
use ET\Builder\Packages\Module\Options\Css\CssStyle;

/**
 * Class that handles "ContactForm7 Module" module.
 */
class ContactForm7 implements DependencyInterface
{


	/**
	 * Register module.
	 * `DependencyInterface` interface ensures class method name `load()` is executed for initialization.
	 */
	public function load()
	{
		add_action('init', array(self::class, 'register_module'));
		add_action('init', array($this, 'register_wordpress_shortcodes'));
		add_action('wp_ajax_dsm_contact_form', array($this, 'render_form'));
		add_action('divi_visual_builder_assets_before_enqueue_scripts', array($this, 'load_contact_forms'));
		add_action('wpcf7_init', array($this, 'dsm_remove_cf7_default_select_form_tag'), 9);
		add_action('wpcf7_init', array($this, 'dsm_register_custom_shortcodes'));
		add_action('wpcf7_init', array($this, 'dsm_register_cf7_select_form_tag'), 11);

		// Also try to register CF7 shortcodes early if CF7 is already loaded
		add_action('init', array($this, 'dsm_register_custom_shortcodes'), 20);
	}

	/**
	 * Register WordPress shortcodes.
	 */
	public function register_wordpress_shortcodes()
	{
		// Register our custom submit shortcode with WordPress
		add_shortcode('dsm-submit', array($this, 'dsm_submit_wp_shortcode_handler'));

		// Add more WordPress shortcodes here as needed:
		// add_shortcode( 'dsm-text', array( $this, 'dsm_text_wp_shortcode_handler' ) );
		// add_shortcode( 'dsm-textarea', array( $this, 'dsm_textarea_wp_shortcode_handler' ) );
	}

	/**
	 * Register our custom CF7 shortcodes.
	 */
	public function dsm_register_custom_shortcodes()
	{
		// Only register if CF7 is available
		if (!function_exists('wpcf7_add_form_tag')) {
			return;
		}

		// Check if shortcode type exists (with fallback for older CF7 versions)
		$shortcode_exists = function_exists('wpcf7_form_tag_type_exists')
			? wpcf7_form_tag_type_exists('dsm-submit')
			: false;

		// Register our custom submit shortcode if not already done
		if (!$shortcode_exists) {
			wpcf7_add_form_tag('dsm-submit', array($this, 'dsm_submit_shortcode_handler'), true);
		}

		// Add more custom shortcodes here as needed:
		// wpcf7_add_form_tag( 'dsm-text', array( $this, 'dsm_text_shortcode_handler' ), true );
		// wpcf7_add_form_tag( 'dsm-textarea', array( $this, 'dsm_textarea_shortcode_handler' ), true );
	}

	/**
	 * Unhook Contact Form 7 default select tag so we can wrap selects for custom arrow styling.
	 *
	 * @return void
	 */
	public function dsm_remove_cf7_default_select_form_tag()
	{
		remove_action('wpcf7_init', 'wpcf7_add_form_tag_select', 10);
	}

	/**
	 * Register select / select* form tags with wrapper class used by module styles (Divi-style chevron).
	 *
	 * @return void
	 */
	public function dsm_register_cf7_select_form_tag()
	{
		if (!function_exists('wpcf7_add_form_tag')) {
			return;
		}

		wpcf7_add_form_tag(
			array('select', 'select*'),
			array($this, 'dsm_cf7_select_form_tag_handler'),
			array(
				'name-attr' => true,
				'selectable-values' => true,
			)
		);
	}

	/**
	 * Renders CF7 select markup with `dsm-contact-form-7-select` wrapper (matches D4 / Divi contact field arrow pattern).
	 *
	 * @param object $tag WPCF7_FormTag instance.
	 * @return string HTML.
	 */
	public function dsm_cf7_select_form_tag_handler($tag)
	{
		if (empty($tag->name)) {
			return '';
		}

		$validation_error = wpcf7_get_validation_error($tag->name);
		$class = wpcf7_form_controls_class($tag->type);

		if ($validation_error) {
			$class .= ' wpcf7-not-valid';
		}

		$atts = array();

		$atts['class'] = $tag->get_class_option($class . ' et_pb_contact_select input');
		$atts['id'] = $tag->get_id_option();
		$atts['tabindex'] = $tag->get_option('tabindex', 'signed_int', true);
		$atts['autocomplete'] = $tag->get_option(
			'autocomplete',
			'[-0-9a-zA-Z]+',
			true
		);

		if ($tag->is_required()) {
			$atts['aria-required'] = 'true';
		}

		if ($validation_error) {
			$atts['aria-invalid'] = 'true';
			$atts['aria-describedby'] = wpcf7_get_validation_error_reference($tag->name);
		} else {
			$atts['aria-invalid'] = 'false';
		}

		$multiple = $tag->has_option('multiple');
		$include_blank = $tag->has_option('include_blank');
		$first_as_label = $tag->has_option('first_as_label');

		if ($tag->has_option('size')) {
			$size = $tag->get_option('size', 'int', true);
			if ($size) {
				$atts['size'] = $size;
			} elseif ($multiple) {
				$atts['size'] = 4;
			} else {
				$atts['size'] = 1;
			}
		}

		if ($data = (array) $tag->get_data_option()) {
			$tag->values = array_merge($tag->values, array_values($data));
			$tag->labels = array_merge($tag->labels, array_values($data));
		}

		$values = $tag->values;
		$labels = $tag->labels;
		$default_choice = $tag->get_default_option(
			null,
			array(
				'multiple' => $multiple,
			)
		);

		if ($include_blank || empty($values)) {
			array_unshift(
				$labels,
				__('&#8212;Please choose an option&#8212;', 'contact-form-7')
			);
			array_unshift($values, '');
		} elseif ($first_as_label) {
			$values[0] = '';
		}

		$html = '';
		$hangover = wpcf7_get_hangover($tag->name);

		foreach ($values as $key => $value) {
			if ($hangover) {
				$selected = in_array($value, (array) $hangover, true);
			} else {
				$selected = in_array($value, (array) $default_choice, true);
			}

			$item_atts = array(
				'value' => $value,
				'selected' => $selected,
			);

			$label = isset($labels[$key]) ? $labels[$key] : $value;

			$html .= sprintf(
				'<option %1$s>%2$s</option>',
				wpcf7_format_atts($item_atts),
				esc_html($label)
			);
		}

		$atts['multiple'] = (bool) $multiple;
		$atts['name'] = $tag->name . ($multiple ? '[]' : '');

		return sprintf(
			'<span class="wpcf7-form-control-wrap dsm-contact-form-7-select" data-name="%1$s"><select %2$s>%3$s</select>%4$s</span>',
			esc_attr($tag->name),
			wpcf7_format_atts($atts),
			$html,
			$validation_error
		);
	}

	/**
	 * Custom handler for [dsm-submit] shortcode (CF7 version).
	 */
	public function dsm_submit_shortcode_handler($tag)
	{

		if (empty($tag->name)) {
			return '';
		}

		$class = wpcf7_form_controls_class($tag->type);
		$class .= ' et_pb_button has-spinner dsm-custom-submit';

		$atts = array(
			'class' => $class,
			'id' => $tag->get_id_option(),
			'tabindex' => $tag->get_option('tabindex', 'signed_int', true),
			'type' => 'submit',
		);

		$value = $tag->values[0] ?? __('Submit', 'contact-form-7');
		$atts_str = wpcf7_format_atts($atts);

		return sprintf(
			'<button %s>%s<span class="dsm-spinner"></span></button>',
			$atts_str,
			esc_html($value)
		);
	}

	/**
	 * WordPress shortcode handler for [dsm-submit] shortcode.
	 *
	 * @param array  $atts    Shortcode attributes.
	 * @param string $content Shortcode content.
	 * @return string The rendered shortcode output.
	 */
	public function dsm_submit_wp_shortcode_handler($atts, $content = '')
	{
		// Parse shortcode attributes

		$attributes = shortcode_atts(
			array(
				'class' => '',
				'id' => '',
				'tabindex' => '',
				'type' => 'submit',
				'text' => (!empty($atts[0]) ? $atts[0] : __('Submit', 'contact-form-7')),
			),
			$atts
		);

		// Build CSS classes
		$class = 'et_pb_button has-spinner dsm-custom-submit';
		if (!empty($attributes['class'])) {
			$class .= ' ' . sanitize_html_class($attributes['class']);
		}

		// Build button attributes
		$button_atts = array(
			'class' => $class,
			'type' => sanitize_text_field($attributes['type']),
		);

		if (!empty($attributes['id'])) {
			$button_atts['id'] = sanitize_html_class($attributes['id']);
		}

		if (!empty($attributes['tabindex'])) {
			$button_atts['tabindex'] = intval($attributes['tabindex']);
		}

		// Use content if provided, otherwise use text attribute
		$button_text = !empty($content) ? $content : $attributes['text'];

		// Build attributes string
		$atts_str = '';
		foreach ($button_atts as $key => $value) {
			if (!empty($value)) {
				$atts_str .= sprintf(' %s="%s"', esc_attr($key), esc_attr($value));
			}
		}

		return sprintf(
			'<p><button%s><span class="dsm-button-text">%s</span><span class="dsm-spinner"></span></button></p>',
			$atts_str,
			esc_html($button_text)
		);
	}

	/**
	 * Get the Contact Form 7 form object.
	 *
	 * @param string $contact_form_id The form ID.
	 * @return \WPCF7_ContactForm|null The contact form object or null if not found.
	 */
	private static function get_contact_form($contact_form_id)
	{
		if (empty($contact_form_id)) {
			return null;
		}

		return wpcf7_contact_form($contact_form_id);
	}

	/**
	 * Get the form with processed shortcodes.
	 *
	 * @param \WPCF7_ContactForm $contact_form The contact form object.
	 * @param array               $attrs        The module attributes.
	 * @return string The form HTML with processed shortcodes.
	 */
	private static function get_processed_form_html($contact_form, $attrs = array())
	{
		if (is_null($contact_form)) {
			return '';
		}

		// Get the original form content (shortcodes)
		$form_content = $contact_form->prop('form');

		// Process the shortcodes before CF7 renders them
		$processed_content = self::process_form_shortcodes($form_content, $attrs);

		// Temporarily replace the form content
		$original_form = $contact_form->prop('form');
		$contact_form->set_properties(array('form' => $processed_content));

		// Get the HTML with our processed shortcodes
		$form_html = $contact_form->form_html();

		// Restore the original form content
		$contact_form->set_properties(array('form' => $original_form));

		return $form_html;
	}

	/**
	 * Process and replace shortcodes in the form content.
	 *
	 * @param string $form_content The original form content with shortcodes.
	 * @param array  $attrs        The module attributes.
	 * @return string The processed form content with custom shortcodes.
	 */
	private static function process_form_shortcodes($form_content, $attrs = array())
	{
		if (empty($form_content)) {
			return '';
		}

		// Replace submit shortcodes with our custom ones
		$processed_content = self::replace_submit_shortcodes($form_content, $attrs);

		// Add more shortcode replacements here as needed:
		// $processed_content = self::replace_text_shortcodes( $processed_content, $attrs );
		// $processed_content = self::replace_textarea_shortcodes( $processed_content, $attrs );

		return $processed_content;
	}

	/**
	 * Replace [submit] shortcodes with [dsm-submit] shortcodes.
	 *
	 * @param string $form_content The form content.
	 * @param array  $attrs        The module attributes.
	 * @return string The form content with replaced shortcodes.
	 */
	private static function replace_submit_shortcodes($form_content, $attrs = array())
	{

		// Use the general shortcode replacement method
		$result = self::replace_shortcodes_in_content(
			$form_content,
			array('submit' => 'dsm-submit')
		);

		return $result;
	}

	/**
	 * Replace shortcodes in general WordPress content.
	 *
	 * @param string $content The content to process.
	 * @param array  $replacements Array of shortcode replacements.
	 * @return string The processed content.
	 */
	public static function replace_shortcodes_in_content($content, $replacements = array())
	{
		// Default replacements
		$default_replacements = array(
			'submit' => 'dsm-submit',
			// Add more replacements as needed:
			// 'text' => 'dsm-text',
			// 'textarea' => 'dsm-textarea',
		);

		$replacements = wp_parse_args($replacements, $default_replacements);

		/**
		 * Filter: dsm_shortcode_replacements
		 * 
		 * Allows developers to modify the shortcode replacement mappings.
		 * 
		 * @param array  $replacements Array of shortcode replacements (old => new).
		 * @param string $content      The content being processed.
		 */
		$replacements = apply_filters('dsm_shortcode_replacements', $replacements, $content);

		foreach ($replacements as $old_shortcode => $new_shortcode) {
			// Replace shortcodes while preserving all attributes and content
			$pattern = '/\[' . preg_quote($old_shortcode, '/') . '(\s[^\]]*?)?\]/';
			$replacement = '[' . $new_shortcode . '$1]';
			$content = preg_replace($pattern, $replacement, $content);
		}

		/**
		 * Filter: dsm_processed_shortcode_content
		 * 
		 * Allows developers to modify the content after shortcode replacements.
		 * 
		 * @param string $content      The processed content.
		 * @param array  $replacements The replacements that were applied.
		 */
		return apply_filters('dsm_processed_shortcode_content', $content, $replacements);
	}

	/**
	 * Generate the final form container HTML.
	 *
	 * @param string $form_html The form HTML.
	 * @return string The final container HTML.
	 */
	private static function generate_form_container($form_html)
	{
		return HTMLUtility::render(
			array(
				'tag' => 'div',
				'attributes' => array(
					'class' => 'et_pb_module_inner dsm-cf7-container',
				),
				'childrenSanitizer' => 'et_core_esc_previously',
				'children' => $form_html,
			)
		);
	}

	/**
	 * Renders the form in the ajax request.
	 *
	 * @return void
	 */
	public function render_form()
	{
		$nonce = isset( $_REQUEST['nonce'] )
			? sanitize_text_field( wp_unslash( (string) $_REQUEST['nonce'] ) )
			: '';

		if ( '' === $nonce || ! wp_verify_nonce( $nonce, 'render_dsm_form' ) ) {
			exit;
		}

		$contact_form_id = isset( $_REQUEST['id'] ) ? absint( wp_unslash( $_REQUEST['id'] ) ) : 0;
		$contact_form = self::get_contact_form($contact_form_id);

		if ( is_null( $contact_form ) || 0 === $contact_form_id ) {
			echo 'No contact form 7 Selected';
			exit;
		}

		// Ensure CF7 shortcodes are registered for AJAX calls.
		$this->ensure_cf7_shortcodes_registered();

		// Use the same shortcode processing for AJAX requests.
		$result = self::get_processed_form_html($contact_form, array());

		// FALLBACK: If shortcode is still visible, do direct HTML replacement
		if (strpos($result, '[dsm-submit') !== false) {
			$result = $this->fallback_html_replacement($result);
		}

		if ( function_exists( 'et_core_intentionally_unescaped' ) ) {
			echo et_core_intentionally_unescaped( $result, 'Contact Form 7 generated form markup' );
		} else {
			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CF7 generates trusted form markup.
			echo $result;
		}
		exit;
	}

	/**
	 * Fallback HTML replacement for when shortcode processing fails.
	 */
	private function fallback_html_replacement($html)
	{
		// Replace [dsm-submit] shortcode with actual HTML
		$pattern = '/\[dsm-submit([^\]]*)\]/';

		$replacement = function ($matches) {
			// Parse attributes from shortcode
			$attributes = shortcode_parse_atts($matches[1]);
			$text = $attributes[0] ?? 'Submit';
			$class = isset($attributes['class']) ? $attributes['class'] : '';

			return sprintf(
				'<button type="submit" class="et_pb_button has-spinner dsm-custom-submit %s">%s<span class="dsm-spinner"></span></button>',
				esc_attr($class),
				esc_html($text)
			);
		};

		return preg_replace_callback($pattern, $replacement, $html);
	}

	/**
	 * Ensure CF7 shortcodes are registered (for AJAX calls).
	 */
	private function ensure_cf7_shortcodes_registered()
	{
		// Check if CF7 is available and shortcode is not already registered
		if ( function_exists( 'wpcf7_add_form_tag' ) ) {
			// Force registration regardless of existing check (for AJAX context).
			wpcf7_add_form_tag( 'dsm-submit', array( $this, 'dsm_submit_shortcode_handler' ), true );
		}

		// Also register with WordPress shortcode as backup.
		if (!shortcode_exists('dsm-submit')) {
			add_shortcode('dsm-submit', array($this, 'dsm_submit_wp_shortcode_handler'));
		}
	}

	/**
	 * Loads the list of cf7 contact forms.
	 *
	 * @return void
	 */
	public function load_contact_forms()
	{

		$forms = get_posts(
			array(
				'post_type' => 'wpcf7_contact_form',
				'posts_per_page' => -1,
			)
		);

		$processed_forms = array(
			'none' => array(
				'label' => 'Select Contact Form',
			),
		);

		foreach ($forms as $form) {
			$processed_forms[$form->ID] = array(
				'label' => $form->post_title,
			);
		}

		wp_localize_script(
			'dsm-supreme-modules',
			'dsmContactForms',
			array(
				'forms' => $processed_forms,
				'renderNonce' => wp_create_nonce('render_dsm_form'),
			)
		);
	}

	/**
	 * Initilises the module.
	 *
	 * @return void
	 */
	public static function dsm_initialise()
	{
		add_action(
			'divi_module_library_modules_dependency_tree',
			function ($dependency_tree) {
				$dependency_tree->add_dependency(new self());
			}
		);
	}

	/**
	 * Register module.
	 */
	public static function register_module()
	{

		$module_json_path = DSM_DIR_PATH . 'divi-5/public/packages/module-library/contact-form7';

		ModuleRegistration::register_module(
			$module_json_path,
			array(
				'render_callback' => array(self::class, 'render_callback'),
			)
		);
	}

	/**
	 * Render module style.
	 *
	 * @param array $args - Arguments.
	 */
	public static function module_styles($args)
	{
		$attrs = $args['attrs'] ?? array();
		$elements = $args['elements'];

		Style::add(
			array(
				'id' => $args['id'],
				'name' => $args['name'],
				'orderIndex' => $args['orderIndex'],
				'storeInstance' => $args['storeInstance'],
				'styles' => array(
					// Module.
					$elements->style(
						array(
							'attrName' => 'module',
							'styleProps' => array(
								'disabledOn' => array(
									'disabledModuleVisibility' => $args['settings']['disabledModuleVisibility'] ?? null,
								),
							),
						)
					),

					CssStyle::style(
						array(
							'selector' => $args['orderClass'],
							'attr' => $attrs['css'] ?? array(),
						)
					),

					$elements->style(
						array(
							'attrName' => 'labels',
						)
					),

					$elements->style(
						array(
							'attrName' => 'input_textarea_select',
						)
					),

					$elements->style(
						array(
							'attrName' => 'date_field',
						)
					),

					$elements->style(
						array(
							'attrName' => 'date_icon',
						)
					),

					$elements->style(
						array(
							'attrName' => 'error_msg',
						)
					),

					$elements->style(
						array(
							'attrName' => 'placeholder',
						)
					),

					$elements->style(
						array(
							'attrName' => 'radio_checkbox',
						)
					),

					$elements->style(
						array(
							'attrName' => 'button',
						)
					),

					$elements->style(
						array(
							'attrName' => 'file',
						)
					),

					$elements->style(
						array(
							'attrName' => 'file_padding',
						)
					),

					$elements->style(
						array(
							'attrName' => 'error_validation',
						)
					),

					$elements->style(
						array(
							'attrName' => 'validation_success',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .wpcf7 label",
							'attr' => $attrs['label_bottom_spacing']['innerContent'] ?? array(),
							'property' => 'margin-bottom',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .wpcf7-form p:nth-last-of-type(1)",
							'attr' => $attrs['button_alignment']['innerContent'] ?? array(),
							'property' => 'text-align',
						)
					),

					CommonStyle::style(
						array(
							'selector' => "{$args['orderClass']} .wpcf7 form.invalid .wpcf7-response-output, {$args['orderClass']} .wpcf7 form.unaccepted .wpcf7-response-output",
							'attr' => $attrs['button_alignment']['innerContent'] ?? array(),
							'property' => 'text-align',
						)
					),

				),
			)
		);
	}

	/**
	 * Render module script data.
	 *
	 * @param array $args - Arguments.
	 */
	public static function module_script_data($args)
	{
		$elements = $args['elements'];

		// Element Script Data Options.
		$elements->script_data(
			array(
				'attrName' => 'module',
			)
		);
	}

	/**
	 * Render module classnames.
	 *
	 * @param array $args - Arguments.
	 */
	public static function module_classnames($args)
	{
		$classnames_instance = $args['classnamesInstance'];
		$attrs = $args['attrs'];

		// Module.
		$classnames_instance->add(
			ElementClassnames::classnames(
				array(
					'attrs' => $attrs['module']['decoration'] ?? array(),
				)
			)
		);
	}

	/**
	 * Render module HTML output.
	 *
	 * @param array     $attrs - Modules Attributes.
	 * @param string    $content - Content.
	 * @param \WP_Block $block - Block.
	 * @param mixed     $elements - Elements.
	 */
	public static function render_callback($attrs, $content, $block, $elements)
	{

		if (!dsm_is_cf7_active()) {
			return '';
		}

		$contact_form_id = $attrs['cf7_library']['innerContent']['desktop']['value'] ?? '';
		$contact_form = self::get_contact_form($contact_form_id);

		if (is_null($contact_form)) {
			return '';
		}

		// Get the form HTML with processed shortcodes
		$form_html = self::get_processed_form_html($contact_form, $attrs);

		// Generate the final form container
		$module_inner = self::generate_form_container($form_html);

		// This are the module elements that will be rendered in the frontend.
		$module_elements = $elements->style_components(
			array(
				'attrName' => 'module',
			)
		);

		// This are the children of the module container, which are the module elements and the module inner.
		$module_container_children = $module_elements . $module_inner;

		return Module::render(
			array(
				// FE only.
				'orderIndex' => $block->parsed_block['orderIndex'],
				'storeInstance' => $block->parsed_block['storeInstance'],

				// VB equivalent.
				'attrs' => $attrs,
				'elements' => $elements,
				'id' => $block->parsed_block['id'],
				'name' => $block->block_type->name,
				'classnamesFunction' => array(self::class, 'module_classnames'),
				'moduleCategory' => $block->block_type->category,
				'stylesComponent' => array(self::class, 'module_styles'),
				'scriptDataComponent' => array(self::class, 'module_script_data'),
				'children' => $module_container_children,
			)
		);
	}
}