<?php
/**
 * AhaPay Callback Handler
 *
 * @package AhaPay
 * @since 1.0.0
 */

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

/**
 * AhaPay_Callback_Handler Class
 *
 * Handles incoming webhook callbacks from AhaPay payment gateway.
 */
class Ahapaybuynowpaylater_Callback_Handler {
	/**
	 * AhaPay API key
	 *
	 * @var string
	 */
	private $api_key;

	/**
	 * Gateway instance
	 *
	 * @var Ahapaybuynowpaylater_WC_Gateway
	 */
	private $gateway;

	/**
	 * Constructor
	 */
	public function __construct() {
		// Support both the new and old gateway class names during rename
		if (!class_exists('Ahapaybuynowpaylater_WC_Gateway')) {
			throw new \RuntimeException('Ahapaybuynowpaylater gateway class not found. Please ensure the plugin is installed and activated.');
		}
		$this->gateway = new Ahapaybuynowpaylater_WC_Gateway();
		$this->api_key = $this->gateway->get_option('api_key');

		// Register the webhook endpoint
		add_action('rest_api_init', [$this, 'register_webhook_endpoint']);
	}


	/**
	 * Register the webhook endpoint
	 */
	public function register_webhook_endpoint() {
		register_rest_route('ahapaybuynowpaylater/v1', '/callback', [
			'methods' => 'POST',
			'callback' => [$this, 'handle_callback'],
			'permission_callback' => [$this, 'verify_webhook_request'],
		]);
		$this->log_info('Registered webhook endpoint', [
			'route' => '/wp-json/ahapaybuynowpaylater/v1/callback'
		]);
	}

	/**
	 * Verify the webhook request
	 *
	 * @param WP_REST_Request $request The incoming request
	 * @return bool Whether the request is valid
	 */
	public function verify_webhook_request(WP_REST_Request $request) {
		try {
			// Log basic request metadata
			$remote_ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
			$this->log_info('Incoming webhook request', [
				'method' => $request->get_method(),
				'path' => $request->get_route(),
				'content_type' => $request->get_header('content-type'),
				'remote_ip' => $remote_ip,
				'content_length' => (int) ($request->get_header('content-length') ?: strlen((string) $request->get_body())),
			]);

			// Extract the signature from the header (try different header formats)
			$signature = $request->get_header('x_api_key_hash');
			if (empty($signature)) {
				$signature = $request->get_header('x-api-key-hash');
			}
			if (empty($signature)) {
				$signature = $request->get_header('HTTP_X_API_KEY_HASH');
			}

			// Log headers (sanitized)
			$headers = $request->get_headers();
			$sanitized_headers = $this->sanitize_headers_for_log($headers);
			$this->log_info('Request headers (sanitized)', $sanitized_headers);

			if (empty($signature)) {
				$this->log_error('Missing API key signature header', [ 'gateway_env' => $this->gateway->get_option('environment') ]);
				return false;
			}

			// Verify the signature format
			if (!preg_match('/^([a-f0-9]{64})$/i', $signature))  {
				$this->log_error('Invalid API key signature format', [
					'signature_preview' => $this->mask($signature)
				]);
				return false;
			}

			// Compare the hash of the API key with the signature
			$hashed_api_key = $this->hash_api_key();
			if (!hash_equals($hashed_api_key, $signature)) {
				$this->log_error('Invalid API key signature', [
					'expected' => $this->mask($hashed_api_key),
					'provided' => $this->mask($signature)
				]);
				return false;
			}

			$this->log_info('Webhook signature verified');
			return true;
		} catch (Exception $e) {
			$this->log_error('Verification error', ['exception' => $e->getMessage()]);
			return false;
		}
	}

	private function hash_api_key() {
		return hash('sha256', $this->api_key);
	}

	/**
	 * Handle webhook callback
	 *
	 * @param WP_REST_Request $request The incoming request
	 * @return WP_REST_Response The response
	 */
	public function handle_callback(WP_REST_Request $request) {
		try {
			$this->log_info('Handling webhook callback: parsing payload', [
				'content_type' => $request->get_header('content-type'),
				'body_length' => strlen((string) $request->get_body())
			]);

			$payload = $request->get_json_params();

			if (empty($payload['reference_number']) || empty($payload['status'])) {
				$this->log_error('Invalid payload', [ 'payload_keys' => is_array($payload) ? array_keys($payload) : 'non-array' ]);
				return new WP_Error('invalid_payload', 'Invalid payload', ['status' => 400]);
			}

			// Extract the necessary data from the payload
			$order_id = sanitize_text_field($payload['reference_number']);
			$status = sanitize_text_field($payload['status']);
			$transaction_id = sanitize_text_field($payload['online_order_id'] ?? '');

			$this->log_info('Callback payload received', [
				'order_id' => $order_id,
				'status' => $status,
				'transaction_id' => $transaction_id
			]);

			// Get the order
			$order = wc_get_order($order_id);
			if (!$order) {
				$this->log_error('Order not found', ['order_id' => $order_id]);
				return new WP_Error('order_not_found', 'Order not found', ['status' => 404]);
			}
			$this->log_info('Order loaded', [
				'order_id' => $order_id,
				'current_status' => $order->get_status()
			]);

			// Process the payment status
			$this->log_info('Processing payment status', [
				'order_id' => $order_id,
				'incoming_status' => $status,
				'transaction_id' => $transaction_id
			]);
			$result = $this->process_payment_status($order, $status, $transaction_id);
			$this->log_info('Status processing completed', [ 'result' => is_wp_error($result) ? 'error' : 'success' ]);
			if (is_wp_error($result)) {
				return new WP_REST_Response(['error' => 'Status handling failed'], 400);
			}

			return new WP_REST_Response([
				'success' => true,
				'message' => 'Callback processed successfully'
			], 200);

		} catch (Exception $e) {
			$this->log_error('Callback error', ['exception' => $e->getMessage()]);
			return new WP_REST_Response([
				'success' => false,
				'error' => $e->getMessage()
			],  500);

		}
	}

	/**
	 * Process the payment status and update the order accordingly
	 *
	 * @param WC_Order $order The order object
	 * @param string $status The payment status
	 * @param string $transaction_id The transaction ID
	 * @return bool|WP_Error True on success, WP_Error on failure
	 */
	private function process_payment_status($order, $status, $transaction_id) {
		// Prevent duplicate processing
		$duplicate = $this->validate_transaction_id_callback($order, $transaction_id);
		$this->log_info('Duplicate transaction check', [
			'order_id' => $order->get_id(),
			'existing_tx' => (string) $order->get_meta('_ahapaybuynowpaylater_order_id'),
			'incoming_tx' => (string) $transaction_id,
			'is_duplicate' => $duplicate ? 'yes' : 'no'
		]);
		if ($duplicate) {
			return new WP_Error('invalid_transaction_id_callback', 'Callback already processed', ['status' => 400]);
		}

		// Status handler instance
		$this->log_info('Invoking status handler', [
			'order_id' => $order->get_id(),
			'status' => $status,
			'transaction_id' => $transaction_id
		]);
		$ahapay_status_handler = new Ahapaybuynowpaylater_Status_Handler($this->gateway);
		$handle_status = $ahapay_status_handler->handle_status($order, $status, $transaction_id);


		if (!$handle_status) {
			$this->log_error('Failed to handle payment status', [
				'order_id' => $order->get_id(),
				'transaction_id' => $transaction_id,
				'status' => $status
			]);
			return new WP_Error('invalid_status', 'Invalid payment status', ['status' => 400]);
		}

		$this->log_info('Payment status handled successfully', [
			'order_id' => $order->get_id(),
			'new_status' => $order->get_status(),
			'transaction_id' => $transaction_id
		]);
		return true;
	}

	/**
	 * Validate the transaction ID to prevent duplicate processing
	 *
	 * @param WC_Order $order The order object
	 * @param string $transaction_id The transaction ID from the callback
	 * @return bool True if the transaction ID is valid, false otherwise
	 */
	private function validate_transaction_id_callback($order, $transaction_id) {
		$existing_transaction_id = $order->get_meta('_ahapaybuynowpaylater_order_id');
		return !empty($existing_transaction_id) && $existing_transaction_id !== $transaction_id;
	}

	/**
	 * Log error messages
	 *
	 * @param string $message The error message
	 * @param array $context Additional context data
	 */
	private function log_error($message, array $context = []) {
		if (method_exists($this->gateway, 'log')) {
			$this->gateway->log($this->format_log($message, $context), 'error');
		}
	}

	/**
	 * Log info messages (respects gateway debug setting)
	 *
	 * @param string $message
	 * @param array $context
	 */
	private function log_info($message, array $context = []) {
		if (method_exists($this->gateway, 'log')) {
			$this->gateway->log($this->format_log($message, $context), 'info');
		}
	}

	/**
	 * Format log message with optional JSON-encoded context
	 *
	 * @param string $message
	 * @param array $context
	 * @return string
	 */
	private function format_log($message, array $context = []) {
		if (empty($context)) {
			return (string) $message;
		}
		return (string) $message . ' | context=' . wp_json_encode($context);
	}

	/**
	 * Sanitize headers for safe logging (mask sensitive values)
	 *
	 * @param array $headers
	 * @return array
	 */
	private function sanitize_headers_for_log(array $headers) {
		$sanitized = [];
		foreach ($headers as $key => $value) {
			$val = is_array($value) ? (string) reset($value) : (string) $value;
			$lower = strtolower((string) $key);
			if ($lower === 'x_api_key_hash' || $lower === 'x-api-key-hash' || $lower === 'http_x_api_key_hash') {
				$val = $this->mask($val);
			}
			$sanitized[$key] = $val;
		}
		return $sanitized;
	}

	/**
	 * Mask a sensitive string leaving last N chars visible
	 *
	 * @param string $value
	 * @param int $showEnd
	 * @return string
	 */
	private function mask($value, $showEnd = 6) {
		$value = (string) $value;
		$len = strlen($value);
		if ($len <= $showEnd) {
			return str_repeat('*', max(0, $len));
		}
		return str_repeat('*', $len - $showEnd) . substr($value, -$showEnd);
	}
}
