<?php
/**
 * Utility helpers for ZainPay WooCommerce Gateway.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class PGZW_Utils {
    /**
     * Verify ZainPay signature against raw request body using secret.
     *
     * @param string $body             Raw request body (JSON)
     * @param string $secret           Webhook secret
     * @param string $signature_header Signature header value
     * @return bool
     */
    public static function verify_zainpay_signature( $body, $secret, $signature_header ) {
        if ( empty( $secret ) || empty( $signature_header ) ) {
            return false;
        }
        $expected_signature = hash_hmac( 'sha256', (string) $body, (string) $secret );
        return hash_equals( $expected_signature, (string) $signature_header );
    }

    /**
     * Parse WooCommerce order ID from a ZainPay reference.
     *
     * References the plugin generates carry the order ID inline as
     * "WC|{order_id}|{timestamp}". The inline (popup) SDK discards that and
     * substitutes its own random reference, so anything that does not match
     * falls back to a lookup against the reference recorded on the order.
     *
     * @param string $reference
     * @return int Order ID or 0 if not found
     */
    public static function parse_order_id_from_reference( $reference ) {
        if ( ! is_string( $reference ) || '' === $reference ) {
            return 0;
        }

        if ( strpos( $reference, '|' ) !== false ) {
            $parts = explode( '|', $reference );
            if ( count( $parts ) >= 2 && strtoupper( $parts[0] ) === 'WC' ) {
                return absint( $parts[1] );
            }
        }

        return self::find_order_id_by_txn_ref( $reference );
    }

    /**
     * Look up an order by the ZainPay transaction reference recorded against it.
     *
     * @param string $reference
     * @return int Order ID or 0 if not found
     */
    public static function find_order_id_by_txn_ref( $reference ) {
        if ( ! is_string( $reference ) || '' === $reference || ! function_exists( 'wc_get_orders' ) ) {
            return 0;
        }

        $orders = wc_get_orders(
            array(
                'limit'      => 1,
                'return'     => 'ids',
                'meta_key'   => '_zainpay_txn_ref', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
                'meta_value' => $reference, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
            )
        );

        return empty( $orders ) ? 0 : absint( $orders[0] );
    }

    /**
     * Compute net and fee based on order total and amount received (net after fees).
     * Fee is clamped to >= 0.
     *
     * @param float $order_total
     * @param float $amount_received
     * @return array{net: float, fee: float}
     */
    public static function compute_net_and_fee( $order_total, $amount_received ) {
        $order_total     = (float) $order_total;
        $amount_received = (float) $amount_received;
        $fee             = max( 0.0, $order_total - $amount_received );
        return array(
            'net' => $amount_received,
            'fee' => $fee,
        );
    }

    /**
     * Normalize raw amount based on expected order total, applying a heuristic
     * to convert from kobo (x100) to Naira if the raw amount appears 100x larger.
     *
     * @param float $order_total
     * @param float $raw_amount
     * @return float
     */
    public static function normalize_amount_for_order_total( $order_total, $raw_amount ) {
        $order_total = (float) $order_total;
        $raw_amount  = (float) $raw_amount;
        if ( $order_total > 0 && $raw_amount > ( $order_total * 10 ) ) {
            return $raw_amount / 100.0;
        }
        return $raw_amount;
    }

    /**
     * Sanitize an array recursively, redacting sensitive fields and sanitizing scalar values.
     * This is useful for sanitizing $_GET, $_POST, and other user input arrays before logging.
     *
     * @param array $data The array to sanitize
     * @return array The sanitized array
     */
    public static function sanitize_array( $data ) {
        if ( ! is_array( $data ) ) {
            return is_scalar( $data ) ? sanitize_text_field( $data ) : '';
        }

        $sanitized        = array();
        $sensitive_fields = array(
            'card_number',
			'cvv',
			'cvc',
			'expiry',
			'expiry_month',
			'expiry_year',
            'pin',
			'password',
			'secret',
			'token',
			'api_key',
			'auth',
			'authorization',
            'credit_card',
			'cc_number',
			'cc_cvv',
			'cc_exp',
			'security_code',
        );

        foreach ( $data as $key => $value ) {
            $key = sanitize_text_field( $key );

            // Redact sensitive fields
            if ( in_array( strtolower( $key ), $sensitive_fields, true ) ) {
                $sanitized[ $key ] = '[REDACTED]';
                continue;
            }

            // Recursively sanitize arrays
            if ( is_array( $value ) ) {
                $sanitized[ $key ] = self::sanitize_array( $value );
            } else {
                // Sanitize scalar values
                $sanitized[ $key ] = is_scalar( $value ) ? sanitize_text_field( $value ) : '';
            }
        }

        return $sanitized;
    }
}