<?php
/**
 * Generic Integration Provider
 *
 * Handles integrations purely through JSON configuration without custom code.
 *
 * @package SureForms
 * @since 1.13.0
 */

namespace SRFM_Pro\Inc\Pro\Native_Integrations;

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

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

/**
 * Generic Provider class.
 *
 * @since 1.13.0
 */
class Generic_Provider extends Integration_Provider {
	/**
	 * Current context for placeholder replacement
	 *
	 * @var array
	 * @since 1.13.0
	 */
	private $current_context = [];

	/**
	 * Test integration connection using JSON configuration
	 *
	 * @param array $credentials User credentials.
	 * @param array $auth_config Authentication configuration.
	 * @return array Test result.
	 * @since 1.13.0
	 */
	public function test_connection( $credentials, $auth_config = [] ) {
		if ( empty( $auth_config['test_endpoint']['url'] ) ) {
			return [
				'success' => false,
				'message' => __( 'Test endpoint URL not configured.', 'sureforms-pro' ),
			];
		}

		// Validate credentials are provided.
		$has_credentials = false;
		if ( is_array( $credentials ) ) {
			foreach ( $credentials as $value ) {
				if ( ! empty( $value ) ) {
					$has_credentials = true;
					break;
				}
			}
		}

		if ( ! $has_credentials ) {
			return [
				'success' => false,
				'message' => __( 'Credentials are required.', 'sureforms-pro' ),
			];
		}

		try {
			// Build test URL with enhanced placeholder processing.
			$test_url = $this->process_url_with_config(
				$auth_config['test_endpoint']['url'],
				$credentials,
				$auth_config['test_endpoint']['url_processing'] ?? []
			);

			// Prepare headers.
			$headers = [];
			if ( ! empty( $auth_config['headers'] ) ) {
				foreach ( $auth_config['headers'] as $key => $value ) {
					$headers[ (string) $key ] = $this->replace_placeholders( $value, $credentials );
				}
			}

			// Make test request.
			$response = $this->make_api_request(
				$test_url,
				$auth_config['test_endpoint']['method'] ?? 'GET',
				$headers
			);

			if ( is_wp_error( $response ) ) {
				return [
					'success' => false,
					// translators: %s: The error message returned from the failed connection attempt.
					'message' => sprintf( __( 'Connection failed: %s', 'sureforms-pro' ), $response->get_error_message() ),
				];
			}

			// @var array<string,mixed> $response HTTP response array from wp_remote_get/wp_remote_post.
			$status_code  = wp_remote_retrieve_response_code( $response );
			$success_code = $auth_config['test_endpoint']['success_criteria']['status_code'] ?? 200;

			if ( $status_code === $success_code ) {
				return [
					'success' => true,
					'message' => __( 'Connection successful!', 'sureforms-pro' ),
				];
			}

			// Try to get error message from response.
			$body          = wp_remote_retrieve_body( $response );
			$data          = json_decode( $body, true );
			$error_message = $this->extract_error_message( Helper::get_array_value( $data ) ) ?? __( 'Connection failed. Please check your credentials.', 'sureforms-pro' );

			return [
				'success' => false,
				'message' => $error_message,
			];

		} catch ( \Exception $e ) {
			return [
				'success' => false,
				// translators: %s: The exception error message from the failed connection test.
				'message' => sprintf( __( 'Connection test error: %s', 'sureforms-pro' ), $e->getMessage() ),
			];
		}
	}

	/**
	 * Fetch select options using JSON configuration
	 *
	 * @param array $credentials User credentials.
	 * @param array $api_config API configuration.
	 * @param array $parameters Optional parameters.
	 * @return array Options array.
	 * @since 1.13.0
	 */
	public function fetch_select_options( $credentials, $api_config, $parameters = [] ) {
		$options = [];

		try {
			// Build URL with enhanced processing.
			$url = $this->process_url_with_config(
				$api_config['url'],
				array_merge( $credentials, $parameters ),
				$api_config['url_processing'] ?? []
			);

			// Prepare headers.
			$headers = [];
			if ( ! empty( $api_config['headers'] ) ) {
				foreach ( $api_config['headers'] as $key => $value ) {
					$headers[ Helper::get_string_value( $key ) ] = $this->replace_placeholders( $value, $credentials );
				}
				$headers['User-Agent'] = 'SureForms Pro/' . SRFM_PRO_VER;
			}

			// Prepare payload for POST requests.
			$payload = null;
			if ( ! empty( $api_config['payload'] ) && in_array( strtoupper( $api_config['method'] ?? 'GET' ), [ 'POST', 'PUT', 'PATCH' ], true ) ) {
				$payload = $api_config['payload'];
			}

			// Make API request.
			$response = $this->make_api_request( $url, $api_config['method'] ?? 'GET', $headers, $payload );

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

			// @var array<string,mixed> $response HTTP response array from wp_remote_get/wp_remote_post.
			$status_code = wp_remote_retrieve_response_code( $response );
			if ( 200 !== $status_code ) {
				return $options;
			}

			$body = wp_remote_retrieve_body( $response );
			// @var array<string,mixed> $data Decoded JSON response data.
			$data = json_decode( $body, true );

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

			// Process response based on configuration.
			$options = $this->process_api_response( Helper::get_array_value( $data ), $api_config['response_type'] ?? '', $api_config, 'select_options' );

		} catch ( \Exception $e ) {
			// Log error using WordPress built-in function.
			_doing_it_wrong( __METHOD__, 'SureForms Generic Provider Error: ' . esc_html( $e->getMessage() ), '1.13.0' );
		}

		return $options;
	}

	/**
	 * Fetch dynamic fields using JSON configuration
	 *
	 * @param array $credentials User credentials.
	 * @param array $api_config API configuration.
	 * @param array $parameters Parameters for the API call.
	 * @return array Fields array.
	 * @since 1.13.0
	 */
	public function fetch_dynamic_fields( $credentials, $api_config, $parameters = [] ) {
		$fields = [];

		// Store context for placeholder replacement.
		$this->current_context = array_merge( $credentials, $parameters );

		try {
			// Build URL with enhanced processing.
			$url = $this->process_url_with_config(
				$api_config['url'],
				array_merge( $credentials, $parameters ),
				$api_config['url_processing'] ?? []
			);

			// Prepare headers.
			$headers = [];
			if ( ! empty( $api_config['headers'] ) ) {
				foreach ( $api_config['headers'] as $key => $value ) {
					$headers[ (string) $key ] = $this->replace_placeholders( $value, $credentials );
				}
			}

			// Prepare payload for POST requests.
			$payload = null;
			if ( ! empty( $api_config['payload'] ) && in_array( strtoupper( $api_config['method'] ?? 'GET' ), [ 'POST', 'PUT', 'PATCH' ], true ) ) {
				$payload = $api_config['payload'];
			}

			// Make API request.
			$response = $this->make_api_request( $url, $api_config['method'] ?? 'GET', $headers, $payload );

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

			// @var array<string,mixed> $response HTTP response array from wp_remote_get/wp_remote_post.
			$status_code = wp_remote_retrieve_response_code( $response );

			if ( 200 !== $status_code ) {
				return $fields;
			}

			$body = wp_remote_retrieve_body( $response );
			// @var array<string,mixed> $data Decoded JSON response data.
			$data = json_decode( $body, true );

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

			// Process response based on configuration.
			$fields = $this->process_api_response( Helper::get_array_value( $data ), $api_config['response_type'] ?? '', $api_config, 'dynamic_fields' );

		} catch ( \Exception $e ) {
			// Log error using WordPress built-in function.
			_doing_it_wrong( __METHOD__, 'SureForms Generic Provider Error: ' . esc_html( $e->getMessage() ), '1.13.0' );
		}

		return $fields;
	}

	/**
	 * Map provider field type to SureForms field type using JSON configuration
	 *
	 * @param string $provider_type Provider field type.
	 * @return string SureForms field type.
	 * @since 1.13.0
	 */
	protected function map_field_type( $provider_type ) {
		$field_mappings = $this->config['field_mappings'] ?? [];
		return $field_mappings[ $provider_type ] ?? 'text';
	}

	/**
	 * Process API response based on response type and configuration
	 *
	 * @param array  $response API response data.
	 * @param string $response_type Type of response to process.
	 * @param array  $config API configuration.
	 * @param string $expected_output Type of output expected (select_options or dynamic_fields).
	 * @return array Processed response.
	 * @since 1.13.0
	 */
	protected function process_api_response( $response, $response_type, $config, $expected_output = '' ) {
		switch ( $response_type ) {
			case 'select_options':
				return $this->map_response_to_options( $response, $config );

			case 'custom_fields':
			case 'dynamic_fields':
				return $this->map_response_to_fields( $response, $config );

			case 'select_options_and_dynamic_fields':
				if ( 'select_options' === $expected_output ) {
					// Return select options when specifically requested.
					return $this->map_response_to_options( $response, $config );
				}
				// Return dynamic fields by default or when specifically requested.
				return $this->map_response_to_fields( $response, $config );

			case 'column_headers':
				return Integration_Utils::map_response_to_column_headers( $response, $config );

			default:
				// Try to auto-detect based on configuration.
				if ( ! empty( $config['response_mapping'] ) ) {
					if ( isset( $config['response_mapping']['field_key'] ) ) {
						return $this->map_response_to_fields( $response, $config );
					}
						return $this->map_response_to_options( $response, $config );

				}
				return [];
		}
	}

	/**
	 * Enhanced URL processing with configuration support
	 *
	 * @param string $url URL template.
	 * @param array  $data Data for replacements.
	 * @param array  $url_processing URL processing configuration.
	 * @return string Processed URL.
	 * @since 1.13.0
	 */
	protected function process_url_with_config( $url, $data, $url_processing ) {
		// Start with basic placeholder replacement.
		$processed_url = $this->replace_placeholders( $url, $data );

		// Apply datacenter extraction if configured.
		if ( ! empty( $url_processing['datacenter_extraction']['enabled'] ) ) {
			$processed_url = $this->apply_datacenter_extraction( $processed_url, $data, $url_processing['datacenter_extraction'] );
		}

		// Apply hash generation if configured.
		if ( ! empty( $url_processing['hash_generation']['enabled'] ) ) {
			$processed_url = $this->apply_hash_generation( $processed_url, $data, $url_processing['hash_generation'] );
		}

		return $processed_url;
	}

	/**
	 * Enhanced placeholder replacement with special processing support
	 *
	 * @param string $template Template string.
	 * @param array  $replacements Replacement values.
	 * @return string Processed string.
	 * @since 1.13.0
	 */
	protected function replace_placeholders( $template, $replacements ) {
		return Integration_Utils::replace_placeholders( $template, $replacements );
	}

	/**
	 * Map API response to options array using JSON configuration
	 *
	 * @param array $data API response data.
	 * @param array $config Configuration with response_mapping.
	 * @return array Options array.
	 * @since 1.13.0
	 */
	private function map_response_to_options( $data, $config ) {
		$options = [];
		$mapping = $config['response_mapping'] ?? $config['map'] ?? [];

		if ( ! isset( $mapping['data'] ) || ! isset( $mapping['value'] ) || ! isset( $mapping['label'] ) ) {
			return $options;
		}

		// Navigate to the data using the path.
		$items = Integration_Utils::get_nested_value( $data, $mapping['data'] );
		if ( ! is_array( $items ) ) {
			return $options;
		}

		// Map each item to option format.
		foreach ( $items as $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}

			// Use get_nested_value for both simple keys and dot notation paths.
			$value = Integration_Utils::get_nested_value( $item, $mapping['value'] ) ?? '';
			$label = Integration_Utils::get_nested_value( $item, $mapping['label'] ) ?? '';

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

		return $options;
	}

	/**
	 * Map API response to fields array using JSON configuration
	 *
	 * @param array $data API response data.
	 * @param array $config Configuration with response_mapping.
	 * @return array Fields array.
	 * @since 1.13.0
	 */
	private function map_response_to_fields( $data, $config ) {
		$fields  = [];
		$mapping = $config['response_mapping'] ?? [];

		if ( ! isset( $mapping['data'] ) || ! isset( $mapping['key'] ) || ! isset( $mapping['label'] ) ) {
			return $fields;
		}

		// Navigate to the data using the path.
		$items = Integration_Utils::get_nested_value( $data, $mapping['data'] );
		if ( ! is_array( $items ) ) {
			return $fields;
		}

		// Handle filtering for specific table/object (e.g., AirTable table filtering).
		if ( ! empty( $mapping['filter_key'] ) && ! empty( $mapping['filter_value'] ) ) {
			$filtered_items = [];
			$filter_key     = $mapping['filter_key'];
			$filter_value   = $mapping['filter_value'];

			// Replace placeholders in filter_value using current_context.
			$filter_value = Integration_Utils::replace_placeholders( $filter_value, $this->current_context );

			foreach ( $items as $item ) {
				if ( is_array( $item ) && isset( $item[ $filter_key ] ) && $item[ $filter_key ] === $filter_value ) {
					$filtered_items[] = $item;
					break; // Only need the first match.
				}
			}
			$items = $filtered_items;
		}

		// Handle nested fields path (e.g., "0.fields" for AirTable).
		if ( ! empty( $mapping['fields_path'] ) && ! empty( $items ) ) {
			$fields_data = Integration_Utils::get_nested_value( $items, $mapping['fields_path'] );
			if ( is_array( $fields_data ) ) {
				$items = $fields_data;
			} else {
				return $fields; // No fields found at the specified path.
			}
		}

		// Check if array is not associative, convert to associative array.
		$items = Integration_Utils::transform_to_key_label_array( $items );

		// Map each item to field format.
		foreach ( $items as $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}

			$field_type_value = Integration_Utils::get_nested_value( $item, $mapping['type'] ?? 'type' ) ?? 'text';
			$field_key        = Integration_Utils::get_nested_value( $item, $mapping['key'] ) ?? '';
			$field_label      = Integration_Utils::get_nested_value( $item, $mapping['label'] ) ?? '';

			// Skip fields without key or label.
			if ( empty( $field_key ) || empty( $field_label ) ) {
				continue;
			}

			$required_raw = Integration_Utils::get_nested_value(
				$item,
				$mapping['required'] ?? 'required'
			);

			$field = [
				'key'         => $field_key,
				'label'       => $field_label,
				'type'        => $this->map_field_type( is_string( $field_type_value ) ? $field_type_value : 'text' ),
				'required'    => filter_var( $required_raw, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ) ?? false,
				'description' => Integration_Utils::get_nested_value( $item, $mapping['description'] ?? 'description' ) ?? '',
			];

			// Add display order if available.
			if ( ! empty( $mapping['display_order'] ) ) {
				$display_order = Integration_Utils::get_nested_value( $item, $mapping['display_order'] );
				if ( null !== $display_order ) {
					$field['display_order'] = $display_order;
				}
			}

			// Handle field options for select/dropdown fields.
			if ( ! empty( $mapping['options'] ) ) {
				$options_data = Integration_Utils::get_nested_value( $item, $mapping['options'] );
				if ( is_array( $options_data ) ) {
					$field['options'] = [];
					foreach ( $options_data as $option ) {
						if ( is_string( $option ) ) {
							$field['options'][] = [
								'label' => $option,
								'value' => $option,
							];
						} elseif ( is_array( $option ) ) {
							$field['options'][] = [
								'label' => $option['label'] ?? $option['name'] ?? $option,
								'value' => $option['value'] ?? $option['id'] ?? $option,
							];
						}
					}
				}
			}

			$fields[] = $field;
		}

		// Sort by display order if available.
		if ( ! empty( $mapping['display_order'] ) ) {
			usort(
				$fields,
				static function( $a, $b ) {
					$order_a = $a['display_order'] ?? 0;
					$order_b = $b['display_order'] ?? 0;
					return $order_a <=> $order_b;
				}
			);
		}

		return $fields;
	}

	/**
	 * Extract error message from API response
	 *
	 * @param array $data API response data.
	 * @return string|null Error message or null if not found.
	 * @since 1.13.0
	 */
	private function extract_error_message( $data ) {
		if ( ! is_array( $data ) ) {
			return null;
		}

		// Common error message fields.
		$error_fields = [ 'error', 'message', 'detail', 'error_description', 'error_message' ];

		foreach ( $error_fields as $field ) {
			if ( isset( $data[ $field ] ) && is_string( $data[ $field ] ) ) {
				return $data[ $field ];
			}
		}

		return null;
	}

	/**
	 * Apply datacenter extraction from API key
	 *
	 * @param string $url URL template.
	 * @param array  $data Data containing source field.
	 * @param array  $config Datacenter extraction configuration.
	 * @return string URL with datacenter replaced.
	 * @since 1.13.0
	 */
	private function apply_datacenter_extraction( $url, $data, $config ) {
		$source_field      = $config['source_field'] ?? '';
		$pattern           = $config['pattern'] ?? '';
		$replacement_token = $config['replacement_token'] ?? '';

		if ( empty( $source_field ) || empty( $pattern ) || empty( $replacement_token ) ) {
			return $url;
		}

		$source_value = $data[ $source_field ] ?? '';
		if ( empty( $source_value ) ) {
			return $url;
		}

		// Extract datacenter using regex pattern.
		if ( preg_match( '/' . $pattern . '/', $source_value, $matches ) ) {
			$datacenter = $matches[1] ?? '';
			if ( ! empty( $datacenter ) ) {
				$url = str_replace( $replacement_token, $datacenter, $url );
			}
		}

		return $url;
	}

	/**
	 * Apply hash generation for URLs
	 *
	 * @param string $url URL template.
	 * @param array  $data Data containing source field.
	 * @param array  $config Hash generation configuration.
	 * @return string URL with hash replaced.
	 * @since 1.13.0
	 */
	private function apply_hash_generation( $url, $data, $config ) {
		$source_field      = $config['source_field'] ?? '';
		$hash_type         = $config['hash_type'] ?? 'md5';
		$replacement_token = $config['replacement_token'] ?? '';

		if ( empty( $source_field ) || empty( $replacement_token ) ) {
			return $url;
		}

		$source_value = $data[ $source_field ] ?? '';
		if ( empty( $source_value ) ) {
			return $url;
		}

		// Generate hash.
		$hash = '';
		switch ( $hash_type ) {
			case 'md5':
				$hash = md5( strtolower( trim( $source_value ) ) );
				break;
		}

		if ( ! empty( $hash ) ) {
			$url = str_replace( $replacement_token, $hash, $url );
		}

		return $url;
	}
}
