<?php
/**
 * Field Manager Service
 *
 * Handles dynamic fields functionality for native integrations.
 *
 * @package SureForms
 * @since 1.13.0
 */

namespace SRFM_Pro\Inc\Pro\Native_Integrations\Services;

use SRFM\Inc\Helper;
use SRFM_Pro\Inc\Pro\Native_Integrations\Integration_Provider;
use SRFM_Pro\Inc\Pro\Native_Integrations\Provider_Factory;
use SRFM_Pro\Inc\Pro\Native_Integrations\Utils\Integration_Utils;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Field Manager Service class.
 *
 * @since 1.13.0
 */
class Field_Manager {
	/**
	 * Config Manager instance
	 *
	 * @var Config_Manager
	 */
	private $config_manager;

	/**
	 * Constructor
	 *
	 * @param Config_Manager $config_manager Config manager instance.
	 * @since 1.13.0
	 */
	public function __construct( Config_Manager $config_manager ) {
		$this->config_manager = $config_manager;
	}

	/**
	 * Get dynamic fields for an integration
	 *
	 * @param \WP_REST_Request $request The REST request.
	 * @return \WP_REST_Response
	 * @since 1.13.0
	 */
	public function get_dynamic_fields( $request ) {
		$integration_name = Helper::get_string_value( $request->get_param( 'integration' ) );
		$action           = Helper::get_string_value( $request->get_param( 'action' ) );

		if ( empty( $integration_name ) || empty( $action ) ) {
			return new \WP_REST_Response(
				[
					'success' => false,
					'message' => __( 'Integration name and action are required.', 'sureforms-pro' ),
				],
				400
			);
		}

		try {
			// Load integration configuration.
			$integration_config = $this->config_manager->load_integration_config( $integration_name );
			if ( ! $integration_config ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => __( 'Integration configuration not found.', 'sureforms-pro' ),
					],
					404
				);
			}

			// Find action configuration.
			$action_config = $this->config_manager->find_action_config( $integration_config, $action );
			if ( ! $action_config ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => __( 'Action configuration not found.', 'sureforms-pro' ),
					],
					404
				);
			}

			// Get stored credentials.
			// WordPress plugin integrations don't require credentials since they access plugins directly.
			$is_wordpress_plugin = isset( $integration_config['provider'] ) && 'WordPress' === $integration_config['provider'];
			$is_plugin_detection = isset( $integration_config['plugin_detection'] );

			$credentials = [];
			if ( ! $is_wordpress_plugin && ! $is_plugin_detection ) {
				$auth_config = $integration_config['auth'] ?? [];
				$credentials = Integration_Utils::get_stored_credentials( $integration_name, $auth_config, null, $this->config_manager );
				if ( is_wp_error( $credentials ) || ! $credentials ) {
					return new \WP_REST_Response(
						[
							'success' => false,
							'message' => __( 'No credentials found. Please configure the integration first.', 'sureforms-pro' ),
						],
						400
					);
				}
			}

			// Get fields from the action configuration.
			$fields = $action_config['fields'] ?? [];

			// Use provider system for enhanced field processing.
			$provider_type = Provider_Factory::get_provider_type( $integration_name );
			$provider      = Provider_Factory::create( $provider_type, $integration_config );

			if ( $provider instanceof Integration_Provider ) {
				$fields = $this->process_fields_with_provider(
					$provider,
					$action_config,
					$credentials,
					$request,
					$fields
				);
			} else {
				// Fallback to legacy processing.
				$fields = $this->process_fields_legacy(
					$action_config,
					$credentials,
					$request,
					$fields
				);
			}

			return new \WP_REST_Response(
				[
					'success' => true,
					'data'    => $fields,
				],
				200
			);

		} catch ( \Exception $e ) {
			return new \WP_REST_Response(
				[
					'success' => false,
					'message' => sprintf(
						/* translators: %s: Exception message */
						__( 'Failed to get dynamic fields: %s', 'sureforms-pro' ),
						$e->getMessage()
					),
				],
				500
			);
		}
	}

	/**
	 * Process fields using provider system
	 *
	 * @param Integration_Provider $provider Provider instance.
	 * @param array                $action_config Action configuration.
	 * @param array                $credentials Integration credentials.
	 * @param \WP_REST_Request     $request The REST request.
	 * @param array                $fields Base fields array.
	 * @return array Processed fields.
	 * @since 1.13.0
	 */
	private function process_fields_with_provider( $provider, $action_config, $credentials, $request, $fields ) {
		// Ensure provider is the correct type.
		if ( ! $provider instanceof Integration_Provider ) {
			return $fields;
		}

		// Use generic API processing.
		$apis = $action_config['apis'] ?? [];

		if ( ! empty( $apis ) ) {
			return $this->process_generic_apis( $provider, $apis, $credentials, $request, $fields );
		}

		// No API configuration found.
		return $fields;
	}

	/**
	 * Process generic APIs structure
	 *
	 * @param Integration_Provider $provider Provider instance.
	 * @param array                $apis APIs configuration.
	 * @param array                $credentials Integration credentials.
	 * @param \WP_REST_Request     $request The REST request.
	 * @param array                $fields Base fields array.
	 * @return array Processed fields.
	 * @since 1.13.0
	 */
	private function process_generic_apis( $provider, $apis, $credentials, $request, $fields ) {
		$parameters = $this->get_request_parameters( $request );

		// Build dependency chain based on available parameters.
		$dependency_chain = $this->build_dependency_chain( $apis, $parameters );

		foreach ( $dependency_chain as $api_name ) {
			$api_config = $apis[ $api_name ];
			$fields     = $this->process_single_api( $provider, $api_config, $credentials, $parameters, $fields );
		}

		return $fields;
	}

	/**
	 * Build dependency processing order
	 *
	 * @param array $apis APIs configuration.
	 * @param array $parameters Available parameters.
	 * @return array Ordered list of API names.
	 * @since 1.13.0
	 */
	private function build_dependency_chain( $apis, $parameters = [] ) {
		$chain     = [];
		$processed = [];

		// First pass: APIs with no dependencies (always include these).
		foreach ( $apis as $api_name => $api_config ) {
			$dependencies = $api_config['dependencies'] ?? [];
			if ( empty( $dependencies ) ) {
				$chain[]     = $api_name;
				$processed[] = $api_name;
			}
		}

		// Subsequent passes: APIs whose dependencies are satisfied by available parameters.
		$iteration = 0;

		$total_apis  = count( $apis );
		$chain_count = count( $chain );
		while ( $chain_count < $total_apis && $iteration < $total_apis ) {
			foreach ( $apis as $api_name => $api_config ) {
				if ( in_array( $api_name, $processed, true ) ) {
					continue;
				}

				$dependencies           = $api_config['dependencies'] ?? [];
				$dependencies_satisfied = true;

				// Check if all dependencies have values in parameters.
				foreach ( $dependencies as $dependency ) {
					if ( empty( $parameters[ $dependency ] ) ) {
						$dependencies_satisfied = false;
						break;
					}
				}

				if ( $dependencies_satisfied ) {
					$chain[]     = $api_name;
					$processed[] = $api_name;
					$chain_count++; // Update the count when chain grows.
				}
			}
			$iteration++;
		}

		return $chain;
	}

	/**
	 * Process a single API configuration
	 *
	 * @param Integration_Provider $provider Provider instance.
	 * @param array                $api_config API configuration.
	 * @param array                $credentials Integration credentials.
	 * @param array                $parameters Request parameters.
	 * @param array                $fields Base fields array.
	 * @return array Processed fields.
	 * @since 1.13.0
	 */
	private function process_single_api( $provider, $api_config, $credentials, $parameters, $fields ) {
		$dependencies = $api_config['dependencies'] ?? [];

		// Check if all dependencies are satisfied.
		if ( ! empty( $dependencies ) ) {
			foreach ( $dependencies as $dependency ) {
				if ( empty( $parameters[ $dependency ] ) ) {
					// Dependencies not satisfied, skip this API.
					return $fields;
				}
			}
		}

		$response_type = $api_config['response_type'] ?? 'select_options';
		$target_fields = $api_config['target_fields'] ?? [];

		switch ( $response_type ) {
			case 'select_options':
				$options = $provider->fetch_select_options( $credentials, $api_config, $parameters );
				$fields  = $this->apply_options_to_target_fields( $fields, $options, $target_fields );
				break;

			case 'dynamic_fields':
				$dynamic_fields = $provider->fetch_dynamic_fields( $credentials, $api_config, $parameters );
				if ( ! empty( $dynamic_fields ) ) {
					$fields = array_merge( $fields, $dynamic_fields );
				}
				break;

			case 'select_options_and_dynamic_fields':
				// First, handle select options for dropdowns.
				$options = $provider->fetch_select_options( $credentials, $api_config, $parameters );
				$fields  = $this->apply_options_to_target_fields( $fields, $options, $target_fields );

				// Then, handle dynamic fields if dependencies are satisfied.
				$dynamic_fields = $provider->fetch_dynamic_fields( $credentials, $api_config, $parameters );
				if ( ! empty( $dynamic_fields ) ) {
					$fields = array_merge( $fields, $dynamic_fields );
				}
				break;
		}

		return $fields;
	}

	/**
	 * Apply options to multiple target fields
	 *
	 * @param array $fields Fields array.
	 * @param array $options Options to apply.
	 * @param array $target_fields Target field keys.
	 * @return array Updated fields array.
	 * @since 1.13.0
	 */
	private function apply_options_to_target_fields( $fields, $options, $target_fields ) {
		foreach ( $fields as &$field ) {
			if ( isset( $field['key'] ) && in_array( $field['key'], $target_fields, true ) ) {
				$field['options'] = $options;
			}
		}
		return $fields;
	}

	/**
	 * Process fields using legacy method
	 *
	 * @param array            $action_config Action configuration.
	 * @param array            $credentials Integration credentials.
	 * @param \WP_REST_Request $request The REST request.
	 * @param array            $fields Base fields array.
	 * @return array Processed fields.
	 * @since 1.13.0
	 */
	private function process_fields_legacy( $action_config, $credentials, $request, $fields ) {
		// Handle lists_api for populating select fields (like list_id dropdown).
		if ( isset( $action_config['lists_api'] ) ) {
			$lists_options = $this->fetch_select_options( $action_config['lists_api'], $credentials );
			$fields        = $this->apply_options_to_fields( $fields, $lists_options, 'list_id' );
		}

		// Handle dynamic_fields_api with parameters.
		if ( isset( $action_config['dynamic_fields_api'] ) ) {
			$parameters      = $this->get_request_parameters( $request );
			$dynamic_options = $this->fetch_dynamic_options(
				$action_config['dynamic_fields_api'],
				$credentials,
				$parameters
			);

			// Apply dynamic options to fields.
			$fields = $this->apply_dynamic_options_to_fields( $fields, $dynamic_options );
		}

		return $fields;
	}

	/**
	 * Get request parameters from the request
	 *
	 * @param \WP_REST_Request $request The REST request.
	 * @return array Parameters array.
	 * @since 1.13.0
	 */
	private function get_request_parameters( $request ) {
		$parameters_json = $request->get_param( 'parameters' );
		$parameters      = [];

		if ( ! empty( $parameters_json ) && is_string( $parameters_json ) ) {
			$decoded    = json_decode( $parameters_json, true );
			$parameters = is_array( $decoded ) ? $decoded : [];
		}

		return $parameters;
	}

	/**
	 * Apply options to specific fields
	 *
	 * @param array  $fields Fields array.
	 * @param array  $options Options to apply.
	 * @param string $target_field Target field key.
	 * @return array Updated fields array.
	 * @since 1.13.0
	 */
	private function apply_options_to_fields( $fields, $options, $target_field ) {
		foreach ( $fields as &$field ) {
			if ( isset( $field['key'] ) && $target_field === $field['key'] ) {
				$field['options'] = $options;
				break;
			}
		}
		return $fields;
	}

	/**
	 * Apply dynamic options to fields
	 *
	 * @param array $fields Fields array.
	 * @param array $options Dynamic options.
	 * @return array Updated fields array.
	 * @since 1.13.0
	 */
	private function apply_dynamic_options_to_fields( $fields, $options ) {
		foreach ( $fields as &$field ) {
			// Check if this field should receive dynamic options.
			if ( isset( $field['dynamic'] ) && $field['dynamic'] && ! empty( $options ) ) {
				$field['options'] = $options;
			}
		}
		return $fields;
	}

	/**
	 * Fetch select options from API
	 *
	 * @param array $api_config API configuration.
	 * @param array $credentials Integration credentials.
	 * @return array Options array.
	 * @since 1.13.0
	 */
	private function fetch_select_options( $api_config, $credentials ) {
		$options = [];

		try {
			// Build API URL.
			$url = $api_config['url'] ?? '';
			if ( empty( $url ) ) {
				return $options;
			}

			// Replace placeholders in URL.
			$url = Integration_Utils::replace_placeholders( $url, $credentials );

			// Prepare headers.
			$headers = [
				'User-Agent' => 'SureForms Pro/' . SRFM_PRO_VER,
			];

			// Add authentication headers.
			if ( isset( $credentials['api_key'] ) ) {
				$headers['Authorization'] = 'Bearer ' . $credentials['api_key'];
			}

			// Make API request.
			$response = wp_remote_get(
				$url,
				[
					'headers' => $headers,
					'timeout' => 30,
				]
			);

			if ( is_wp_error( $response ) ) {
				return $options;
			}

			$body = wp_remote_retrieve_body( $response );
			$data = json_decode( $body, true );

			if ( JSON_ERROR_NONE !== json_last_error() ) {
				return $options;
			}

			// Map response to options.
			$map     = $api_config['response_map'] ?? [];
			$options = $this->map_response_to_options( Helper::get_array_value( $data ), $map );

		} catch ( \Exception $e ) {
			// Log error using WordPress built-in function but don't break the flow.
			_doing_it_wrong( __METHOD__, 'SureForms Field Manager Error: ' . esc_html( $e->getMessage() ), '1.13.0' );
		}

		return $options;
	}

	/**
	 * Fetch dynamic options from API with parameters
	 *
	 * @param array $dynamic_api_config Dynamic API configuration.
	 * @param array $credentials Integration credentials.
	 * @param array $parameters Request parameters.
	 * @return array Options array.
	 * @since 1.13.0
	 */
	private function fetch_dynamic_options( $dynamic_api_config, $credentials, $parameters = [] ) {
		$options = [];

		try {
			$response_type = $dynamic_api_config['response_type'] ?? 'options';

			if ( 'merge_fields' === $response_type ) {
				// For merge_fields, we need the list_id parameter to fetch the correct merge fields.
				if ( ! empty( $parameters['list_id'] ) ) {
					// Fetch merge fields with list_id parameter.
					$url = str_replace( '{{list_id}}', $parameters['list_id'], $dynamic_api_config['url'] );
					$url = Integration_Utils::replace_placeholders( $url, array_merge( $credentials, $parameters ) );

					// Make API request.
					$response = wp_remote_get(
						$url,
						[
							'headers' => [
								'Authorization' => 'Bearer ' . $credentials['api_key'],
								'User-Agent'    => 'SureForms Pro/' . SRFM_PRO_VER,
							],
							'timeout' => 30,
						]
					);

					if ( ! is_wp_error( $response ) ) {
						$body = wp_remote_retrieve_body( $response );
						$data = json_decode( $body, true );

						if ( JSON_ERROR_NONE === json_last_error() ) {
							$options = $this->map_merge_fields_to_options( Helper::get_array_value( $data ) );
						}
					}
				}
			}
		} catch ( \Exception $e ) {
			// Log error using WordPress built-in function but don't break the flow.
			_doing_it_wrong( __METHOD__, 'SureForms Field Manager Error: ' . esc_html( $e->getMessage() ), '1.13.0' );
		}

		return $options;
	}

	/**
	 * Map API response to options array
	 *
	 * @param array $data API response data.
	 * @param array $map Response mapping configuration.
	 * @return array Options array.
	 * @since 1.13.0
	 */
	private function map_response_to_options( $data, $map ) {
		$options = [];

		if ( empty( $map ) || ! is_array( $data ) ) {
			return $options;
		}

		// Get the data array path.
		$data_path = $map['data_path'] ?? '';
		$items     = $data;

		if ( ! empty( $data_path ) ) {
			$items = $this->extract_nested_value( $data, $data_path );
		}

		if ( ! is_array( $items ) ) {
			return $options;
		}

		// Map each item to option format.
		$value_key = $map['value_key'] ?? 'id';
		$label_key = $map['label_key'] ?? 'name';

		foreach ( $items as $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}

			$value = $this->extract_nested_value( $item, $value_key );
			$label = $this->extract_nested_value( $item, $label_key );

			if ( null !== $value && null !== $label ) {
				$options[] = [
					'value' => $value,
					'label' => $label,
				];
			}
		}

		return $options;
	}

	/**
	 * Map merge fields to options format
	 *
	 * @param array $data Merge fields data from API.
	 * @return array Options array.
	 * @since 1.13.0
	 */
	private function map_merge_fields_to_options( $data ) {
		$fields = [];

		if ( ! isset( $data['merge_fields'] ) || ! is_array( $data['merge_fields'] ) ) {
			return $fields;
		}

		foreach ( $data['merge_fields'] as $merge_field ) {
			if ( ! is_array( $merge_field ) ) {
				continue;
			}

			$field = [
				'key'         => $merge_field['tag'] ?? '',
				'label'       => $merge_field['name'] ?? '',
				'type'        => $this->map_merge_field_type( $merge_field['type'] ?? 'text' ),
				'required'    => $merge_field['required'] ?? false,
				'description' => $merge_field['help_text'] ?? '',
			];

			// Handle field options for dropdown/radio fields.
			if ( isset( $merge_field['options'] ) && is_array( $merge_field['options'] ) ) {
				// Handle select/dropdown fields with choices.
				if ( isset( $merge_field['options']['choices'] ) && is_array( $merge_field['options']['choices'] ) ) {
					$field['options'] = [];
					foreach ( $merge_field['options']['choices'] as $choice ) {
						$field['options'][] = [
							'label' => $choice,
							'value' => $choice,
						];
					}
				} else {
					// Handle other option types (like address fields).
					$field['options'] = $merge_field['options'];
				}
			}

			// Add default value if present.
			if ( ! empty( $merge_field['default_value'] ) ) {
				$field['default'] = $merge_field['default_value'];
			}

			$fields[] = $field;
		}

		return $fields;
	}

	/**
	 * Map Mailchimp merge field type to SureForms field type
	 *
	 * @param string $mailchimp_type Mailchimp field type.
	 * @return string SureForms field type.
	 * @since 1.13.0
	 */
	private function map_merge_field_type( $mailchimp_type ) {
		$type_mapping = [
			'text'     => 'text',
			'email'    => 'email',
			'number'   => 'number',
			'dropdown' => 'select',
			'radio'    => 'radio',
			'date'     => 'date',
			'address'  => 'text',
			'phone'    => 'tel',
			'url'      => 'url',
			'imageurl' => 'url',
			'zip'      => 'text',
			'birthday' => 'date',
		];

		return $type_mapping[ $mailchimp_type ] ?? 'text';
	}

	/**
	 * Extract nested value from array using dot notation
	 *
	 * @param array  $array Source array.
	 * @param string $path Dot notation path.
	 * @return mixed Value or null if not found.
	 * @since 1.13.0
	 */
	private function extract_nested_value( $array, $path ) {
		$keys  = explode( '.', $path );
		$value = $array;

		foreach ( $keys as $key ) {
			if ( ! is_array( $value ) || ! isset( $value[ $key ] ) ) {
				return null;
			}
			$value = $value[ $key ];
		}

		return $value;
	}
}
