<?php
/**
 * Calculation main class file.
 *
 * @since 1.5.0
 * @package sureforms-pro
 */

namespace SRFM_Pro\Inc\Business;

use SRFM\Inc\Helper;
use SRFM_Pro\Inc\Helper as Pro_Helper;
use SRFM_Pro\Inc\Traits\Get_Instance;

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

/**
 * Load Calculation feature related functionalities.
 *
 * @since 1.5.0
 */
class Calculation {
	use Get_Instance;

	/**
	 * Initialize Calculation.
	 *
	 * @since 1.5.0
	 */
	public function __construct() {
		add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_scripts' ] );
		add_action( 'srfm_enqueue_block_scripts', [ $this, 'enqueue_calculation_scripts' ] );

		// Add calculation class and required config.
		add_filter( 'srfm_field_classes', [ $this, 'add_calculation_class' ], 10, 2 );
		add_filter( 'srfm_field_config', [ $this, 'add_calculation_config' ], 10, 2 );
		add_filter( 'srfm_register_post_meta', [ $this, 'register_submit_button_enable_meta' ] );
		add_filter( 'srfm_show_submit_button', [ $this, 'show_submit_button' ], 10, 2 );
		add_filter( 'render_block', [ $this, 'filter_block_content' ], 10, 2 );

		add_filter( 'srfm_show_options_values', [ $this, 'show_options_values' ], 10, 2 );
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] );
		add_filter( 'srfm_ai_form_generator_body', [ $this, 'add_calculation_form_version' ], 10, 2 );
		add_filter( 'srfm_ai_form_builder_modify_merged_attributes', [ $this, 'map_number_field_attrs' ], 10, 4 );
		add_filter( 'srfm_modify_ai_post_metas', [ $this, 'create_metas_for_ai' ], 10, 2 );
		add_filter( 'srfm_ai_field_modify_field_type', [ $this, 'field_modify_field_type' ], 10, 4 );
		add_filter( 'srfm_ai_field_map_skip_fields', [ $this, 'skip_fields_in_ai_builder' ], 10, 3 );
		add_filter( 'srfm_ai_form_builder_post_content', [ $this, 'modify_post_content' ], 10, 3 );
	}

	/**
	 * Modify post content for calculator form.
	 *
	 * @param string $post_content The post content.
	 * @param bool   $is_conversational The Conversational Form status.
	 * @param string $form_type The form type.
	 *
	 * @since 1.5.0
	 * @return string
	 */
	public function modify_post_content( $post_content, $is_conversational, $form_type ) {
		// Return the original content if the form is conversational or not a calculator form.
		if ( $is_conversational || 'calculator' !== $form_type ) {
			return $post_content;
		}

		// Extract all SRFM form fields from the post content.
		$form_fields = $this->extract_form_fields( $post_content );

		// Retrieve only the formulas from fields that have calculations enabled.
		$calculation_formulas = $this->get_calculation_formulas( $form_fields );

		// Retrieve slugs for fields that do not contain a calculation formula.
		$all_field_slugs = $this->get_all_field_slugs( $form_fields );

		// Determine which slugs are not used in any calculation formula.
		$unused_slugs = $this->get_unused_slugs( $all_field_slugs, $calculation_formulas );

		// Remove any unused fields from the post content.
		return $this->remove_unused_fields_from_content( $post_content, $unused_slugs );
	}

	/**
	 * Extracts SRFM form blocks and decodes their JSON attributes into arrays.
	 *
	 * @param string $post_content The post content.
	 * @return array<array<string,mixed>> An array of form fields.
	 */
	public function extract_form_fields( $post_content ) {
		// Use regex to find all SRFM form blocks in the post content.
		preg_match_all( '/<!-- wp:srfm\/[^ ]+ ({.*?}) \/-->/', $post_content, $matches );

		if ( empty( $matches[1] ) ) {
			return [];
		}

		return array_map(
			static function ( $json ) {
				$decoded = json_decode( $json, true );
				return is_array( $decoded ) ? $decoded : [];
			},
			$matches[1]
		);
	}

	/**
	 * Retrieves calculation formulas from form fields that have calculations enabled.
	 *
	 * @param array $form_fields The form fields.
	 *
	 * @since 1.5.0
	 * @return array<string> An array of calculation formulas.
	 */
	public function get_calculation_formulas( array $form_fields ) {
		return array_column(
			array_filter(
				$form_fields,
				static fn( $field ) => is_array( $field ) && ! empty( $field['enableCalculation'] )
			),
			'calculationFormula'
		);
	}

	/**
	 * Retrieves the slugs of fields that are have no calculation formula.
	 *
	 * @param array $form_fields The form fields.
	 *
	 * @since 1.5.0
	 * @return array An array of field slugs.
	 */
	public function get_all_field_slugs( array $form_fields ) {
		return array_column(
			array_filter(
				$form_fields,
				static fn( $field ) => is_array( $field ) && empty( $field['calculationFormula'] )
			),
			'slug'
		);
	}

	/**
	 * Filters field slugs to determine which ones are unused in any calculation formula.
	 *
	 * @param array $field_slugs The field slugs.
	 * @param array $formulas The calculation formulas.
	 *
	 * @since 1.5.0
	 * @return array<string> An array of unused field slugs.
	 */
	public function get_unused_slugs( array $field_slugs, array $formulas ) {
		return array_filter(
			$field_slugs,
			static function ( $slug ) use ( $formulas ) {
				if ( empty( $slug ) ) {
					return false;
				}

				// Return true only if the slug is not found in any formula.
				return ! array_reduce(
					$formulas,
					static fn( $found, $formula ) => $found || strpos( $formula, '{form:' . Helper::get_string_value( $slug ) . '}' ) !== false,
					false
				);
			}
		);
	}

	/**
	 * Removes SRFM blocks from the content if their slugs are in the skip list.
	 *
	 * @param string $content The post content.
	 * @param array  $slugs_to_skip The slugs to skip.
	 *
	 * @since 1.5.0
	 * @return string The modified content.
	 */
	public function remove_unused_fields_from_content( $content, $slugs_to_skip ) {
		$result = preg_replace_callback(
			'/<!-- wp:srfm\/[^ ]+ ({.*?}) \/-->/',
			static function ( $matches ) use ( $slugs_to_skip ) {
				$decoded = json_decode( $matches[1], true );
				return is_array( $decoded ) && in_array( $decoded['slug'] ?? '', $slugs_to_skip, true ) ? '' : $matches[0];
			},
			$content
		);

		return $result ? $result : '';
	}

	/**
	 * Skip fields in AI builder from being added in the calculation form.
	 *
	 * @param array<string,mixed> $skip_fields The skip fields.
	 * @param bool                $is_conversational The Conversational Form status.
	 * @param string              $form_type The form type.
	 *
	 * @since 1.5.0
	 * @return array<string|mixed>
	 */
	public function skip_fields_in_ai_builder( $skip_fields, $is_conversational, $form_type ) {

		if ( $is_conversational || 'calculator' !== $form_type ) {
			return $skip_fields;
		}

		return [
			'checkbox',
			'gdpr',
			'input',
			'email',
			'url',
			'textarea',
			'checkbox',
			'gdpr',
			'phone',
			'dropdown',
			'address',
			'hidden',
			'rating',
			'upload',
			'date-picker',
			'time-picker',
			'page-break',
		];
	}

	/**
	 * Modify field type for Calculator Form.
	 * In case of dropdown field type, if the number of options is less than or equal to 6, then change the field type to multi-choice.
	 *
	 * @param string              $field_type The field type.
	 * @param array<string,mixed> $field_data The field data.
	 * @param bool                $is_conversational The Conversational Form status.
	 * @param string              $form_type The form type.
	 *
	 * @since 1.4.0
	 * @return string
	 */
	public function field_modify_field_type( $field_type, $field_data, $is_conversational, $form_type ) {
		if ( $is_conversational || empty( $form_type ) || 'calculator' !== $form_type ) {
			return $field_type;
		}

		// If the field type is dropdown and the number of options is less than or equal to 6, then change the field type to multi-choice.
		if ( 'dropdown' === $field_type && is_array( $field_data['fieldOptions'] ) && 6 >= count( $field_data['fieldOptions'] ) ) {
			$field_type = 'multi-choice';
		}

		return $field_type;
	}

	/**
	 * Create metas for AI when form type is calculator.
	 *
	 * @param array<string,mixed> $metas The metas.
	 * @param array<string,mixed> $form_info_obj The form info object.
	 *
	 * @since 1.5.0
	 * @return array<string,mixed>
	 */
	public function create_metas_for_ai( $metas, $form_info_obj ) {

		if ( ! is_object( $form_info_obj ) || 'calculator' !== $form_info_obj->form_type ) {
			return $metas;
		}

		return [
			'_srfm_submit_button_disable' => true,
		];
	}

	/**
	 * Map number field attributes for calculations.
	 *
	 * @param array<string,mixed> $merged_attributes Merged field attributes.
	 * @param array<string,mixed> $question contains the field attributes data that comes from AI.
	 * @param bool                $is_conversational Whether the form is conversational or not.
	 * @param string              $form_type The form type.
	 *
	 * @since 1.5.0
	 * @return array<string,mixed>
	 */
	public function map_number_field_attrs( $merged_attributes, $question, $is_conversational, $form_type ) {

		if ( $is_conversational && 'calculator' !== $form_type && 'number' !== $question['fieldType'] ) {
			return $merged_attributes;
		}

		$merged_attributes['prefix'] = ! empty( $question['prefix'] ) ? $question['prefix'] : '';
		$merged_attributes['suffix'] = ! empty( $question['suffix'] ) ? $question['suffix'] : '';

		// when the calculation formula is not empty, enable calculation for that field.
		if ( ! empty( $question['calculationFormula'] ) ) {
			$merged_attributes['enableCalculation']  = true;
			$merged_attributes['calculationFormula'] = ! empty( $question['calculationFormula'] ) ? $question['calculationFormula'] : '';
			$merged_attributes['calculationRound']   = ! empty( $question['calculationRound'] ) ? filter_var( $question['calculationRound'], FILTER_VALIDATE_INT ) : 0;
		}

		return $merged_attributes;
	}

	/**
	 * Enqueue Admin Scripts.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	public function enqueue_admin_scripts() {
		$script = [
			'unique_file'        => 'srfmCalculationAdmin',
			'unique_handle'      => 'srfm-calculation-admin',
			'extra_dependencies' => [],
		];

		$script_dep_path = SRFM_PRO_DIR . 'dist/package/business/' . $script['unique_file'] . '.asset.php';
		$script_dep_data = file_exists( $script_dep_path )
			? include $script_dep_path
			: [
				'dependencies' => [],
				'version'      => SRFM_PRO_VER,
			];
		$script_dep      = array_merge( $script_dep_data['dependencies'], $script['extra_dependencies'] );

		// Scripts.
		wp_enqueue_script(
			SRFM_PRO_SLUG . '-' . $script['unique_handle'], // Handle.
			SRFM_PRO_URL . 'dist/package/business/' . $script['unique_file'] . '.js',
			$script_dep, // Dependencies, defined above.
			$script_dep_data['version'], // SRFM_VER.
			true // Enqueue the script in the footer.
		);

		// Register script translations.
		Pro_Helper::register_script_translations( SRFM_PRO_SLUG . '-' . $script['unique_handle'] );
	}

	/**
	 * Add calculations form version to the AI form generator request body.
	 *
	 * @param array<string,mixed> $body The body that contains the user prompt.
	 * @param array<string,mixed> $params The parameters related to the AI form generator.
	 *
	 * @since 1.5.0
	 * @return array<string,mixed>
	 */
	public function add_calculation_form_version( $body, $params ) {

		if ( ! is_array( $params ) || 'calculator' !== $params['form_type'] ) {
			return $body;
		}

		return array_merge(
			$body,
			[
				'type'    => 'calculator',
				'version' => 'calculator',
			]
		);
	}

	/**
	 * Enqueue Admin Scripts.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	public function enqueue_block_editor_scripts() {
		$script = [
			'unique_file'        => 'srfmCalculation',
			'unique_handle'      => 'srfm-calculation',
			'extra_dependencies' => [],
		];

		$script_dep_path = SRFM_PRO_DIR . 'dist/package/business/' . $script['unique_file'] . '.asset.php';
		$script_dep_data = file_exists( $script_dep_path )
			? include $script_dep_path
			: [
				'dependencies' => [],
				'version'      => SRFM_PRO_VER,
			];
		$script_dep      = array_merge( $script_dep_data['dependencies'], $script['extra_dependencies'] );

		// Scripts.
		wp_enqueue_script(
			SRFM_PRO_SLUG . '-' . $script['unique_handle'], // Handle.
			SRFM_PRO_URL . 'dist/package/business/' . $script['unique_file'] . '.js',
			$script_dep, // Dependencies, defined above.
			$script_dep_data['version'], // SRFM_VER.
			true // Enqueue the script in the footer.
		);

		// Register script translations.
		Pro_Helper::register_script_translations( SRFM_PRO_SLUG . '-' . $script['unique_handle'] );
	}

	/**
	 * Enqueue Calculation Scripts.
	 *
	 * @param array<string, mixed> $args Block arguments containing block_name and attr.
	 *
	 * @since 1.5.0
	 * @return void
	 */
	public function enqueue_calculation_scripts( $args ) {
		// Static flag to ensure scripts are only enqueued once.
		static $scripts_enqueued = false;

		// Return early if scripts are already enqueued.
		if ( $scripts_enqueued ) {
			return;
		}

		// Check if this block has calculation enabled.
		$is_calculation_enabled = is_array( $args ) && isset( $args['attr'] ) && is_array( $args['attr'] ) && isset( $args['attr']['enableCalculation'] ) && $args['attr']['enableCalculation'];

		// Return early if this block doesn't have calculation enabled.
		if ( ! $is_calculation_enabled ) {
			return;
		}

		// Load the math.js library.
		wp_enqueue_script(
			SRFM_PRO_SLUG . '-mathjs', // Handle.
			SRFM_PRO_URL . 'assets/js/minified/deps/math.min.js',
			[],
			SRFM_PRO_VER,
			true // Enqueue the script in the footer.
		);

		// Load the frontend custom app script.
		wp_enqueue_script(
			SRFM_PRO_SLUG . '-calculation-app-frontend', // Handle.
			SRFM_PRO_URL . 'dist/package/business/srfmCalculationFrontend.js',
			[],
			SRFM_PRO_VER,
			true // Enqueue the script in the footer.
		);

		// Mark scripts as enqueued.
		$scripts_enqueued = true;
	}

	/**
	 * Register Submit Button Enable Meta.
	 *
	 * @param array<string, mixed> $meta Meta data.
	 *
	 * @since 1.5.0
	 * @return array
	 */
	public function register_submit_button_enable_meta( $meta ) {
		$meta['_srfm_submit_button_disable'] = 'boolean';

		return $meta;
	}

	/**
	 * Show Submit Button.
	 *
	 * @param bool   $should_show_submit_button Should show submit button.
	 * @param number $id                        Form ID.
	 *
	 * @since 1.5.0
	 * @return bool
	 */
	public function show_submit_button( $should_show_submit_button, $id ) {
		$submit_button_disable = get_post_meta( intval( $id ), '_srfm_submit_button_disable', true );

		return $submit_button_disable ? false : $should_show_submit_button;
	}

	/**
	 * Add Calculation class.
	 *
	 * @param string       $field_classes Field classes.
	 * @param array<mixed> $args          Field args.
	 * @since 1.5.0
	 * @return string $field_classes Field classes.
	 */
	public function add_calculation_class( $field_classes, $args ) {
		if ( is_array( $args ) && isset( $args['attributes']['enableCalculation'] ) && $args['attributes']['enableCalculation'] ) {
			if ( is_string( $field_classes ) && ! empty( $field_classes ) ) {
				$field_classes .= ' srfm-calculation';
			} elseif ( empty( $field_classes ) ) {
				$field_classes = 'srfm-calculation';
			}
		}

		return $field_classes;
	}

	/**
	 * Add Calculation config.
	 *
	 * @param array<mixed> $field_config Field config.
	 * @param array<mixed> $args         Field args.
	 * @since 1.5.0
	 * @return array<mixed> $args Field args.
	 */
	public function add_calculation_config( $field_config, $args ) {
		if ( is_array( $args ) && isset( $args['attributes']['calculationFormula'] ) && is_string( $args['attributes']['calculationFormula'] ) && isset( $args['attributes']['enableCalculation'] ) && $args['attributes']['enableCalculation'] ) {
			$formula = $args['attributes']['calculationFormula'];

			// In the formula we have curly braces, we need to replace them with double curly braces.
			$formula = str_replace( '{', '%{%', $formula );
			$formula = str_replace( '}', '%}%', $formula );

			$field_config['calculationFormula'] = $formula;

			// Add rounding options. attributes.calculationRound should be number.
			if ( isset( $args['attributes']['calculationRound'] ) && is_numeric( $args['attributes']['calculationRound'] ) ) {
				$field_config['calculationRound'] = intval( $args['attributes']['calculationRound'] );
			}

			// If block is 'srfm/advanced-heading' then add the numberFormatType.
			if ( isset( $args['blockName'] ) && 'srfm/advanced-heading' === $args['blockName'] ) {
				$field_config['numberFormatType'] = $args['attributes']['numberFormatType'] ?? '';
			}
		}

		return $field_config;
	}

	/**
	 * Show Options Values.
	 *
	 * @param bool $default_value Should show values.
	 * @param bool $value       Value.
	 *
	 * @since 1.5.0
	 * @return bool
	 */
	public function show_options_values( $default_value, $value ) {
		return $value ? true : $default_value;
	}

	/**
	 * Filter Block Content.
	 *
	 * @param string $block_content Entire Block Content.
	 * @param array  $block         Block Properties As An Array.
	 *
	 * @since 1.5.0
	 * @return string
	 */
	public function filter_block_content( $block_content, $block ) {
		if ( isset( $block['blockName'] ) && 'srfm/advanced-heading' === $block['blockName'] ) {
			/**
			 * This script checks if the block attribute 'enableCalculation' is set to true.
			 * If true, it adds a calculation result span to the block content, which includes a placeholder for the calculation result.
			 */
			if ( isset( $block['attrs']['enableCalculation'] ) && $block['attrs']['enableCalculation'] && strpos( $block_content, '{calculation-result}' ) !== false ) {
				$block_content = str_replace( '{calculation-result}', '<span class="srfm-calculation-result"></span>', $block_content );
			}
		}

		return $block_content;
	}
}
