<?php

if ( ! defined( 'ABSPATH' ) ) {
	die( 'You are not allowed to call this page directly.' );
}

/**
 * @since 2.04
 */
class FrmProEntryFormatter extends FrmEntryFormatter {

	/**
	 * Track the field index in the field values. This is used when generating table content.
	 *
	 * @since 6.25
	 *
	 * @var int
	 */
	protected static $current_field_index = 0;

	/**
	 * @var FrmProEntryValues
	 *
	 * @since 2.04
	 */
	protected $entry_values;

	/**
	 * Table style.
	 *
	 * @var string
	 */
	protected $table_style;

	/**
	 * Set the entry_values property
	 *
	 * @since 2.04
	 *
	 * @param array $atts
	 */
	protected function init_entry_values( $atts ) {
		$entry_atts         = $this->prepare_entry_attributes( $atts );
		$this->entry_values = new FrmProEntryValues( $this->entry->id, $entry_atts );
	}

	/**
	 * Which fields to exclude
	 *
	 * @since 3.0
	 */
	protected function skip_fields() {
		$skip_pro_fields = array(
			'break',
			'divider',
			'end_divider',
			'form',
			'password',
			'credit_card',
		);

		return array_merge( parent::skip_fields(), $skip_pro_fields );
	}

	/**
	 * Set the include_extras property
	 *
	 * @since 2.04
	 *
	 * @param array $atts
	 */
	protected function init_include_extras( $atts ) {
		parent::init_include_extras( $atts );

		foreach ( $this->include_extras as $key => $field_type ) {
			if ( in_array( $field_type, array( 'section', 'heading' ), true ) ) {
				$this->include_extras[ $key ] = 'divider';
			} elseif ( $field_type === 'page' ) {
				$this->include_extras[ $key ] = 'break';
			}
		}
	}

	/**
	 * Initialize the single_cell_fields property
	 *
	 * @since 3.0
	 */
	protected function init_single_cell_fields() {
		parent::init_single_cell_fields();

		$single_cell_fields = array(
			'break',
			'divider',
		);

		$this->single_cell_fields = array_merge( $this->single_cell_fields, $single_cell_fields );
	}

	protected function init_table_generator( $atts ) {
		if ( empty( $atts['table_style'] ) || 'classic' === $atts['table_style'] ) {
			parent::init_table_generator( $atts );
		} else {
			$this->table_style     = $atts['table_style'];
			$this->table_generator = FrmEmailStylesController::get_table_generator( $this->table_style );
		}
	}

	/**
	 * Adds field values to content.
	 *
	 * @since 6.25
	 *
	 * @param string $content The content.
	 */
	protected function add_field_values_to_content( &$content ) {
		// Reset the current field index tracking.
		self::$current_field_index = 0;

		parent::add_field_values_to_content( $content );
	}

	/**
	 * Add a field value to the HTML table or plain text content
	 *
	 * @since 2.04
	 *
	 * @param FrmProFieldValue $field_value
	 * @param string $content
	 */
	protected function add_field_value_to_content( $field_value, &$content ) {
		$field_type = $field_value->get_field_type();

		if ( $field_value->has_child_entries() ) {
			$this->add_rows_for_field_with_child_entries( $field_value, $content );
		} elseif ( $field_type === 'divider' ) {
			$this->add_section_to_content( $field_value, $content );
		} elseif ( $field_type === 'end_divider' ) {
			$this->remove_section_placeholder( $content );
		} else {
			parent::add_field_value_to_content( $field_value, $content );
		}

		self::$current_field_index++;
	}

	/**
	 * Add a section heading to the content
	 *
	 * @since 3.0
	 *
	 * @param FrmProFieldValue $field_value
	 * @param string $content
	 */
	protected function add_section_to_content( $field_value, &$content ) {
		// Add item meta to $_POST to make `FrmProEntryMeta::is_field_conditionally_hidden()` work.
		if ( ! isset( $_POST['item_meta'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			$_POST['item_meta'] = $field_value->get_entry()->metas;
		}

		if ( ! $this->is_extra_field_included( $field_value ) ) {
			return;
		}

		$content .= $this->section_placeholder();
		parent::add_field_value_to_content( $field_value, $content );
	}

	/**
	 * Split the content at section placeholder and piece back together
	 *
	 * @since 3.0
	 *
	 * @param string $content
	 *
	 * @return void
	 */
	protected function remove_section_placeholder( &$content ) {
		$section_pos = strpos( $content, $this->section_placeholder() );

		if ( $section_pos === false ) {
			return;
		}

		$section_substring = substr( $content, $section_pos );

		// Remove section substring from content
		$content = str_replace( $section_substring, '', $content );

		// Clean up section substring
		$section_substring = str_replace( $this->section_placeholder(), '', $section_substring );

		if ( $this->section_heading_has_children( $section_substring ) ) {
			// Add section substring to content
			$content .= $section_substring;
		}
	}

	/**
	 * @since 3.0
	 *
	 * @param string $section_substring
	 *
	 * @return bool
	 */
	protected function section_heading_has_children( $section_substring ) {
		$has_children = false;

		if ( $this->format === 'plain_text_block' ) {
			$has_children = substr_count( $section_substring, "\r\n" ) > 2;
		} elseif ( $this->format === 'table' ) {
			$has_children = substr_count( $section_substring, '<tr' ) > 1;
		}

		return $has_children;
	}

	/**
	 * Get child values, then add both parent and child if there are child values
	 *
	 * @since 2.04
	 *
	 * @param FrmProFieldValue $field_value
	 * @param string $content
	 */
	protected function add_rows_for_field_with_child_entries( $field_value, &$content ) {
		$child_content = '';
		$this->append_child_entry_values( $field_value, $child_content );

		if ( $child_content === '' ) {
			return;
		}

		$this->append_parent_values( $field_value, $content );
		$content .= $child_content;
	}

	/**
	 * Add an embedded form or repeating section to value list/table
	 *
	 * @since 2.04
	 *
	 * @param FrmProFieldValue $field_value
	 * @param string $content
	 *
	 * @return void
	 */
	protected function append_parent_values( $field_value, &$content ) {
		if ( ! $this->include_field_in_content( $field_value ) ) {
			return;
		}

		if ( $this->is_plain_text ) {
			$this->prepare_plain_text_display_value_for_extra_fields( $field_value, $display_value );
			$this->add_single_value_plain_text_row( $display_value, $content );
		} else {
			$this->prepare_html_display_value_for_extra_fields( $field_value, $display_value );
			$this->add_single_cell_html_row( $display_value, $content );
		}
	}

	/**
	 * Append child entry values to table or plain text content
	 *
	 * @since 2.04
	 *
	 * @param FrmProFieldValue $field_value
	 * @param string $content
	 */
	protected function append_child_entry_values( $field_value, &$content ) {
		$is_repeater = $field_value->is_repeater();

		foreach ( $field_value->get_displayed_value() as $field_values ) {
			$pre_content = $content;

			foreach ( $field_values as $child_field_info ) {
				$child_field_info->prepare_displayed_value( $this->atts );

				if ( isset( $this->table_generator ) ) {
					$this->table_generator->is_child = $is_repeater;
				}

				$this->add_field_value_to_content( $child_field_info, $content );
			}

			if ( isset( $this->table_generator ) ) {
				$this->table_generator->is_child = false;
			}

			if ( $content !== $pre_content && $is_repeater ) {
				$this->add_separator( $content );
			}
		}

		if ( isset( $this->table_generator ) ) {
			$this->table_generator->is_child = false;
		}
	}

	/**
	 * Add an empty line to separate repeater content.
	 *
	 * @since 3.06.01
	 *
	 * @param string $content - The final output for the entry.
	 */
	protected function add_separator( &$content ) {
		if ( $this->format === 'plain_text_block' ) {
			$content .= "\r\n";
		} elseif ( $this->format === 'table' ) {
			$content .= $this->table_generator->generate_single_cell_table_row( '' );
		}
	}

	/**
	 * Prepare the display value for extra fields an HTML table
	 *
	 * @since 2.04
	 *
	 * @param FrmProFieldValue $field_value
	 * @param string|null      $display_value
	 */
	protected function prepare_html_display_value_for_extra_fields( $field_value, &$display_value ) {
		switch ( $field_value->get_field_type() ) {
			case 'divider':
				$display_value = '<h3>' . $field_value->get_field_label() . '</h3>';
				$this->maybe_remove_section_title( $field_value, $display_value );
				break;

			default:
				parent::prepare_html_display_value_for_extra_fields( $field_value, $display_value );
				break;
		}
	}

	/**
	 * Check if a section's title should be hidden in the summary.
	 *
	 * @since 4.03
	 *
	 * @param FrmProFieldValue $field_value FrmProFieldSummaryValue
	 * @param string $display_value
	 *
	 * @return void
	 */
	protected function maybe_remove_section_title( $field_value, &$display_value ) {
		if ( $this->atts['summary'] && $this->is_field_label_hidden( $field_value ) ) {
			$display_value = '';
		}
	}

	/**
	 * @since 4.03
	 *
	 * @param FrmProFieldValue $field_value
	 *
	 * @return bool
	 */
	protected function is_field_label_hidden( $field_value ) {
		$label = $field_value->get_field_option( 'label' );
		return 'hidden' === $label || 'none' === $label;
	}

	/**
	 * Prepare a plain text value for extra fields
	 *
	 * @since 2.04
	 *
	 * @param FrmProFieldValue $field_value
	 * @param string|null      $display_value
	 */
	protected function prepare_plain_text_display_value_for_extra_fields( $field_value, &$display_value ) {
		switch ( $field_value->get_field_type() ) {
			case 'divider':
				$display_value = "\r\n" . $field_value->get_field_label() . "\r\n";
				break;

			default:
				parent::prepare_plain_text_display_value_for_extra_fields( $field_value, $display_value );
				break;
		}
	}

	/**
	 * Push a single field to the values array
	 *
	 * @since 2.04
	 *
	 * @param FrmProFieldValue $field_value
	 * @param array $output
	 */
	protected function push_single_field_to_array( $field_value, &$output ) {
		$field_key       = $this->get_key_or_id( $field_value );
		$field_type      = $field_value->get_field_type();
		$add_child_array = ( 'divider' === $field_type || ( 'form' === $field_type && $this->atts['child_array'] ) ) && $field_value->has_child_entries();

		if ( $add_child_array ) {
			$this->push_children_to_array( compact( 'field_value', 'field_key' ), $output );
		} elseif ( 'form' === $field_type ) {
			$child_values = $field_value->get_displayed_value();

			if ( $child_values ) {
				$child_values = reset( $child_values );
				$this->push_field_values_to_array( $child_values, $output );
			}
		} else {
			parent::push_single_field_to_array( $field_value, $output );
		}
	}

	/**
	 * @since 3.0
	 *
	 * @param array $field_info
	 * @param array $output
	 *
	 * @return void
	 */
	private function push_children_to_array( $field_info, &$output ) {
		$field_key   = $field_info['field_key'];
		$field_value = $field_info['field_value'];

		$output[ $field_key ] = array(
			'form' => $field_value->get_field_option( 'form_select' ),
		);

		$count = 0;

		foreach ( $field_value->get_displayed_value() as $entry_id => $row_values ) {
			$index                          = 'i' . $entry_id;
			$output[ $field_key ][ $index ] = array();
			$this->push_field_values_to_array( $row_values, $output[ $field_key ][ $index ] );

			$this->push_repeating_field_values_to_array( $row_values, $count, $output );

			++$count;
		}
	}

	/**
	 * Add repeating field values to the array so they are not nested within section
	 *
	 * @since 2.04
	 *
	 * @param array $row_values
	 * @param int $index
	 * @param array $output
	 */
	protected function push_repeating_field_values_to_array( $row_values, $index, &$output ) {
		foreach ( $row_values as $field_value ) {
			/* @var FrmProFieldValue $field_value */

			if ( ! $this->include_repeating_field_in_array( $field_value ) ) {
				continue;
			}

			$key = $this->get_key_or_id( $field_value );

			if ( ! isset( $output[ $key ] ) ) {
				$output[ $key ] = array();
			}

			$displayed_value          = $this->prepare_display_value_for_array( $field_value->get_displayed_value() );
			$output[ $key ][ $index ] = $displayed_value;

			if ( $displayed_value === $field_value->get_saved_value() ) {
				continue;
			}

			if ( ! isset( $output[ $key ] ) ) {
				$output[ $key . '-value' ] = array();
			}

			$output[ $key . '-value' ][ $index ] = $field_value->get_saved_value();
		}
	}

	/**
	 * Determine if a repeating field should be included in the array
	 *
	 * @since 2.04
	 *
	 * @param FrmProFieldValue $field_value
	 *
	 * @return bool
	 */
	protected function include_repeating_field_in_array( $field_value ) {
		return $this->is_extra_field( $field_value ) ? $this->is_extra_field_included( $field_value ) : true;
	}

	/**
	 * Prepare a field's display value for an HTML table
	 *
	 * @since 2.04
	 *
	 * @param array|string $display_value
	 * @param string       $type
	 *
	 * @return mixed|string
	 */
	protected function prepare_display_value_for_html_table( $display_value, $type = '' ) {
		if ( $type === 'file' && is_array( $display_value ) ) {
			$display_value = implode( '<br/><br/>', $display_value );
		}

		return parent::prepare_display_value_for_html_table( $display_value, $type );
	}

	protected function flatten_array( $value ) {
		if ( is_array( $value ) && FrmProImages::has_images_options_in_html( $value ) ) {
			return implode( ' ', $value );
		}

		return parent::flatten_array( $value );
	}

	/**
	 * Section placeholder string
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	protected function section_placeholder() {
		return '{section_placeholder}';
	}

	protected function add_html_row( $value_args, &$content ) {
		$value_args['label'] = $this->maybe_process_shortcodes_in_label( $value_args['label'] );

		if ( ! $this->table_style ) {
			parent::add_html_row( $value_args, $content );
			return;
		}

		if ( ! self::$current_field_index ) {
			// Remove the bottom border of <table> tag.
			$content = $this->table_generator->remove_border( $content, 'bottom' );
		}

		$display_value = $this->prepare_display_value_for_html_table( $value_args['value'], $value_args['field_type'] );

		if ( 'compact' === $this->table_style ) {
			$row = $this->table_generator->generate_two_cell_table_row( $value_args['label'], $display_value );
		} else {
			$row = $this->table_generator->generate_single_cell_table_row(
				FrmEmailStylesController::get_content_for_one_column_cell( $value_args['label'], $display_value )
			);
		}

		// Remove border for all rows in Sleek style, or the first row for other styles.
		if ( ! self::$current_field_index || 'sleek' === $this->table_style ) {
			$row = $this->table_generator->remove_border( $row, 'top' );
		}

		$content .= $row;
	}

	protected function maybe_process_shortcodes_in_label( $label ) {
		if ( str_contains( $label, '[' ) ) {
			return $this->process_shortcodes_in_label( $label );
		}
		return $label;
	}

	/**
	 * @param string $label
	 */
	private function process_shortcodes_in_label( $label ) {
		return FrmFieldsHelper::basic_replace_shortcodes( $label, $this->entry->form_id, $this->entry );
	}

	/**
	 * Check if an extra field is included
	 *
	 * @since 5.0.04
	 *
	 * @param FrmProFieldValue $field_value
	 *
	 * @return bool
	 */
	protected function is_extra_field_included( $field_value ) {
		$included = parent::is_extra_field_included( $field_value );

		if ( ! $included ) {
			return $included;
		}

		$field = $field_value->get_field();

		if ( ! isset( $field->temp_id ) ) {
			$field->temp_id = $field->id;
		}

		$errors = array();
		FrmProEntryMeta::validate_no_input_fields( $errors, $field );

		$unset_item_meta = false;

		if ( ! isset( $_POST['item_meta'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			/**
			 * Set $_POST using the data from entry.
			 * FrmProEntryMeta::is_field_conditionally_hidden checks $_POST data.
			 * But in some cases, like after a Stripe link payment redirect, the $_POST data is not set.
			 */
			$_POST['item_meta'] = $field_value->get_entry()->metas;
			$unset_item_meta    = true;
		}

		$included = ! FrmProEntryMeta::is_field_conditionally_hidden( $field );

		if ( $unset_item_meta ) {
			// Revert $_POST meta to previous state.
			unset( $_POST['item_meta'] );
		}

		return $included;
	}
}
