<?php

namespace Bricksforge\ProForms\Actions;

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

// Include Expression Evaluator for calculation field validation
require_once BRICKSFORGE_PATH . '/includes/helper/ExpressionEvaluator.php';

/**
 * Payment Helper
 * 
 * Provides common functionality for all payment actions (Stripe, PayPal, Mollie, etc.)
 * to prevent code duplication and ensure consistent validation across all payment providers.
 * 
 * @since 3.1.8
 */
class PaymentHelper
{
    /**
     * Validate calculation field to prevent price manipulation
     * 
     * This method recalculates the formula server-side and compares it with the client-submitted value.
     * If they don't match, it indicates potential tampering and the payment should be rejected.
     *
     * @param float $incoming_price The price submitted by the client
     * @param string $price_field_setting The form setting containing the price field reference (e.g., 'stripeAmount', 'paypalAmount')
     * @param int $post_id The post ID where the form is located
     * @param object $form The form object with access to form data
     * @return bool True if valid (or not a calculation field), false if manipulation detected
     */
    public static function validate_payment_calculation($incoming_price, $price_field_setting, $post_id, $form)
    {
        if (!isset($incoming_price) || empty($price_field_setting)) {
            // If basic parameters are missing, allow payment (basic validation already done)
            return true;
        }

        $price_field = $price_field_setting;

        // If the price field contains {{ and }}, we remove them to get the field ID
        if (strpos($price_field, '{{') !== false && strpos($price_field, '}}') !== false) {
            $price_field = str_replace('{{', '', $price_field);
            $price_field = str_replace('}}', '', $price_field);
        }

        $field_ids = isset($_POST['fieldIds']) ? $_POST['fieldIds'] : null;

        if (!isset($field_ids) || empty($field_ids)) {
            // If fieldIds are not provided, we can't validate but we allow the payment to proceed
            // This handles cases where amount is a fixed value, not a calculation field
            return true;
        }

        $field_ids = stripslashes($field_ids);
        $field_ids = json_decode($field_ids, true);

        $element_id = isset($field_ids[$price_field]) ? $field_ids[$price_field] : null;

        if (!isset($element_id) || empty($element_id)) {
            // If we can't find the element ID, we allow the payment (could be a fixed value)
            return true;
        }

        $price_field_element = \Bricks\Helpers::get_element_settings($post_id, $element_id);

        if (!isset($price_field_element) || empty($price_field_element)) {
            // If we can't find the element settings, allow the payment (could be a fixed value)
            return true;
        }

        $formula = isset($price_field_element['formula']) ? $price_field_element['formula'] : null;

        if (!$formula) {
            // It's not a calculation field. We can return true (no validation needed)
            return true;
        }

        $empty_to_zero = isset($price_field_element['setEmptyToZero']) ? $price_field_element['setEmptyToZero'] : false;

        // In the formula, we need to replace each {variable} with the actual value
        $formula = self::replace_formula_variables($formula, $form, $empty_to_zero);

        if (!isset($formula) || empty($formula)) {
            // If formula replacement failed, reject the payment for security
            error_log('Bricksforge Payment Security: Formula replacement failed for calculation field validation.');
            return false;
        }

        // Evaluate the formula server-side
        $expression_evaluator = new \Bricksforge\Helper\ExpressionEvaluator();
        $result = $expression_evaluator->evaluate($formula);

        if (!isset($result['result']) || !is_numeric($result['result'])) {
            // If formula evaluation failed, reject payment for security
            error_log('Bricksforge Payment Security: Formula evaluation failed or returned non-numeric result.');
            return false;
        }

        $result = $result['result'];

        // If the incoming price is not numeric (like 15,503), we need to convert it to a float, like 15.503
        if (!is_numeric($incoming_price)) {
            $incoming_price = str_replace(',', '.', $incoming_price);
        }

        $result = floatval($result);

        // Check for raw calculation values (used for better precision)
        $raw_incoming_values = isset($_POST['calculationFieldRawValues']) ? $_POST['calculationFieldRawValues'] : null;

        if (isset($raw_incoming_values) && !empty($raw_incoming_values)) {
            $raw_incoming_values = stripslashes($raw_incoming_values);
            $raw_incoming_values = json_decode($raw_incoming_values, true);

            if (isset($raw_incoming_values[$element_id]) && $incoming_price !== $raw_incoming_values[$element_id]) {
                $incoming_price = $raw_incoming_values[$element_id];
            }
        }

        $incoming_price = floatval($incoming_price);

        // Compare the server-calculated result with the incoming price
        // We need to use a tolerance because floats are represented in binary with limited precision
        $tolerance = 0.000001;

        if (abs($result - $incoming_price) > $tolerance) {
            // SECURITY: Price mismatch detected - possible manipulation attempt
            error_log(sprintf(
                'Bricksforge Payment Security Alert: Price mismatch detected. Server calculated: %s, Client sent: %s, Difference: %s',
                $result,
                $incoming_price,
                abs($result - $incoming_price)
            ));
            return false;
        }

        return true;
    }

    /**
     * Replace formula variables with actual form field values
     * 
     * This method takes a calculation formula and replaces all {variable} placeholders
     * with the actual values from the submitted form data.
     *
     * @param string $formula The formula string with {variable} placeholders
     * @param object $form The form object with access to form data
     * @param bool $empty_to_zero Whether to convert empty values to 0
     * @return string The formula with all variables replaced by their values
     */
    public static function replace_formula_variables($formula, $form, $empty_to_zero = false)
    {
        $formula = bricks_render_dynamic_data($formula);
        $form_data = $form->get_fields();

        // Find each word wrapped by {}. For each field, we need the value and replace it with the value returned by get_form_field_by_id()
        preg_match_all('/{([^}]+)}/', $formula, $matches);

        foreach ($matches[1] as $match) {

            $field_value = $form->get_form_field_by_id($match, $form_data);

            // Handle array values (multiple selections like checkboxes)
            if (is_array($field_value)) {
                $field_value = implode(', ', $field_value);
            }

            // Remove commas from numbers like 1,000.00 -> 1000.00
            if (is_string($field_value) && strpos($field_value, ',') !== false && strpos($field_value, '.') !== false) {
                $field_value = str_replace(',', '', $field_value);
            }

            // If the field value contains multiple values (comma-separated), process each individually
            // This is crucial for checkbox fields with calculationValue set on each option
            if (is_string($field_value) && strpos($field_value, ', ') !== false) {
                $individual_values = explode(', ', $field_value);
                $sum = 0;
                $all_numeric = true;

                foreach ($individual_values as $single_value) {
                    $single_value = trim($single_value);

                    // Try to get calculation value for each individual value
                    $calculation_value = $form->get_calculation_value_by_id($match, $single_value);

                    if ($calculation_value !== null && $calculation_value !== $single_value) {
                        // Use the calculation value
                        $single_value = $calculation_value;
                    }

                    // Convert comma decimal separator to dot if needed
                    if (!is_numeric($single_value) && is_string($single_value) && strpos($single_value, ',') !== false) {
                        $single_value = str_replace(',', '.', $single_value);
                    }

                    if (is_numeric($single_value)) {
                        $sum += floatval($single_value);
                    } else {
                        $all_numeric = false;
                    }
                }

                if ($all_numeric) {
                    $field_value = $sum;
                }
            } else {
                // Single value - try to get calculation value
                $calculation_value = $form->get_calculation_value_by_id($match, $field_value);

                if ($calculation_value !== null) {
                    $field_value = $calculation_value;
                }

                // Check if the value is not numeric but would become numeric if we replace comma with dot
                if (!is_numeric($field_value) && is_string($field_value) && strpos($field_value, ',') !== false) {
                    $converted_value = str_replace(',', '.', $field_value);
                    if (is_numeric($converted_value)) {
                        $field_value = $converted_value;
                    }
                }
            }

            if (isset($field_value) && $field_value !== "" && is_numeric($field_value)) {
                $formula = str_replace('{' . $match . '}', $field_value, $formula);
            } else {
                if ($empty_to_zero) {
                    $formula = str_replace('{' . $match . '}', '0', $formula);
                }
            }
        }

        return $formula;
    }

    /**
     * Validate and sanitize payment amount
     * 
     * @param mixed $amount The amount to validate
     * @return float|false The validated amount as float, or false if invalid
     */
    public static function validate_amount($amount)
    {
        if (!is_numeric($amount) || $amount <= 0) {
            return false;
        }
        return floatval($amount);
    }

    /**
     * Convert amount to cents (smallest currency unit)
     * 
     * Most payment providers (Stripe, PayPal, Mollie) require amounts in cents
     * 
     * @param float $amount The amount in major currency unit (e.g., dollars, euros)
     * @return int The amount in cents
     */
    public static function convert_to_cents($amount)
    {
        return intval(round(floatval($amount) * 100));
    }

    /**
     * Sanitize and validate currency code
     * 
     * @param string $currency The currency code to validate
     * @return string The sanitized currency code in lowercase
     */
    public static function sanitize_currency($currency)
    {
        return strtolower(sanitize_text_field($currency));
    }

    /**
     * Sanitize payment description
     * 
     * @param string $description The description to sanitize
     * @return string The sanitized description
     */
    public static function sanitize_description($description)
    {
        return sanitize_text_field($description);
    }

    /**
     * Sanitize and validate customer email
     * 
     * @param string $email The email to validate
     * @return string|false The sanitized email, or false if invalid
     */
    public static function sanitize_customer_email($email)
    {
        $sanitized = sanitize_email($email);
        
        if (!is_email($sanitized)) {
            return false;
        }
        
        return $sanitized;
    }

    /**
     * Generate a secure session identifier
     * 
     * @param int $length The length of the identifier (default: 32)
     * @return string The generated session identifier
     */
    public static function generate_session_identifier($length = 32)
    {
        return wp_generate_password($length, false);
    }

    /**
     * Format amount for display
     * 
     * @param float $amount The amount to format
     * @param string $currency The currency code
     * @param int $decimals Number of decimal places (default: 2)
     * @return string The formatted amount
     */
    public static function format_amount($amount, $currency = '', $decimals = 2)
    {
        $formatted = number_format($amount, $decimals, '.', ',');
        
        if (!empty($currency)) {
            $formatted .= ' ' . strtoupper($currency);
        }
        
        return $formatted;
    }
}

