<?php
/**
 * Zano Payment Gateway Utilities
 *
 * @package ZanoPaymentGateway
 */

defined( 'ABSPATH' ) || exit;

/**
 * Utilities class for common functions
 */
class AnyCoin_Utilities {

	/**
	 * Create or update plugin database tables
	 */
	public static function create_database_tables() {
		AnyCoin_Database_Manager::create_tables();
	}

	/**
	 * Check for and resolve duplicate transaction hashes
	 */
	public static function check_for_duplicate_transactions() {
		AnyCoin_Database_Manager::resolve_duplicate_transactions();
	}

	/**
	 * Create plugin directories
	 */
	public static function create_plugin_directories() {
		$directories = [
			ANYCOIN_PAYMENT_PLUGIN_DIR . 'assets/images',
			ANYCOIN_PAYMENT_PLUGIN_DIR . 'assets/css',
			ANYCOIN_PAYMENT_PLUGIN_DIR . 'assets/js',
			ANYCOIN_PAYMENT_PLUGIN_DIR . 'logs',
		];

		foreach ( $directories as $dir ) {
			if ( ! file_exists( $dir ) ) {
				wp_mkdir_p( $dir );
			}
		}
	}

	/**
	 * Create default assets
	 */
	public static function create_default_assets() {
		// No-op: avoid writing assets to the plugin folder at runtime.
		return true;
	}

	/**
	 * Schedule cron jobs
	 */
	public static function schedule_cron_jobs() {
		if ( ! wp_next_scheduled( 'anycoin_check_transactions' ) ) {
			wp_schedule_event( time(), 'every_5_minutes', 'anycoin_check_transactions' );
		}

		if ( ! wp_next_scheduled( 'anycoin_cleanup_expired_payments' ) ) {
			wp_schedule_event( time(), 'every_5_minutes', 'anycoin_cleanup_expired_payments' );
		}
	}

	/**
	 * Unschedule cron jobs
	 */
	public static function unschedule_cron_jobs() {
		$timestamp = wp_next_scheduled( 'anycoin_check_transactions' );
		if ( $timestamp ) {
			wp_unschedule_event( $timestamp, 'anycoin_check_transactions' );
		}

		$timestamp = wp_next_scheduled( 'anycoin_cleanup_expired_payments' );
		if ( $timestamp ) {
			wp_unschedule_event( $timestamp, 'anycoin_cleanup_expired_payments' );
		}
	}

	/**
	 * Get valid payment statuses
	 */
	public static function get_payment_statuses() {
		return [
			'pending'   => 'Pending',
			'detected'  => 'Detected',
			'confirmed' => 'Confirmed',
			'expired'   => 'Expired',
			'failed'    => 'Failed',
		];
	}

	/**
	 * Internal logger wrapper using WooCommerce logger.
	 *
	 * @param string $level   Log level (emergency|alert|critical|error|warning|notice|info|debug).
	 * @param string $message Message to log.
	 * @param array  $context Additional context.
	 */
	private static function log( $level, $message, array $context = [] ) {
		if ( function_exists( 'wc_get_logger' ) ) {
			$logger = wc_get_logger();
			$logger->log( $level, $message, array_merge( [ 'source' => 'anycoin-payment-gateway' ], $context ) );
		}
	}

	/**
	 * Update all order statuses - check for expired payments and verify transactions.
	 *
	 * @throws Exception When API URL is not configured.
	 */
	public static function update_all_order_statuses() {
		global $wpdb;
		$table_name    = $wpdb->prefix . AnyCoin_Constants::DB_TABLE_NAME;
		$updated_count = 0;

		self::log( 'info', 'Zano: Starting bulk order status update' );

		$now                = time();
		$twenty_minutes_ago = gmdate( 'Y-m-d H:i:s', $now - ( 20 * 60 ) );
		$five_minutes_ago   = gmdate( 'Y-m-d H:i:s', $now - ( 5 * 60 ) );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Controlled internal query for batch processing; avoid caching to ensure fresh payment state
		$payments_to_check = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT * FROM %i\n" .
				"                WHERE (\n" .
				"                    -- Pending payments older than 20 minutes without tx_hash (to expire)\n" .
				"                    (status = 'pending' AND (tx_hash IS NULL OR tx_hash = '') AND created_at < %s)\n" .
				"                    OR\n" .
				"                    -- Payments with tx_hash that need confirmation checking and haven't been checked recently\n" .
				"                    (status IN ('pending', 'processing') AND tx_hash IS NOT NULL AND tx_hash != '' \n" .
				"                    AND (updated_at < %s OR updated_at IS NULL))\n" .
				"                    OR\n" .
				"                    -- Transactions with tx_hash but missing block information (repair mode)\n" .
				"                    (tx_hash IS NOT NULL AND tx_hash != '' AND received_block IS NULL)\n" .
				"                )\n" .
				"                ORDER BY created_at ASC \n" .
				'                LIMIT 20',
				$wpdb->prefix . AnyCoin_Constants::DB_TABLE_NAME,
				$twenty_minutes_ago,
				$five_minutes_ago
			),
			ARRAY_A
		);

		self::log( 'info', 'Zano: Found ' . count( $payments_to_check ) . ' payments that need checking' );

		if ( empty( $payments_to_check ) ) {
			self::log( 'info', 'Zano: No payments need checking at this time' );
			return 0;
		}

		$gateway_settings = get_option( 'woocommerce_anycoin_payment_settings', [] );
		$api_url          = $gateway_settings['api_url'] ?? '';

		if ( empty( $api_url ) ) {
			self::log( 'error', 'Zano: API URL not configured, cannot verify transactions' );
			throw new Exception( 'API URL not configured' );
		}

		foreach ( $payments_to_check as $payment ) {
			$order = wc_get_order( $payment['order_id'] );
			if ( ! $order ) {
				self::log( 'warning', 'Zano: Order #' . $payment['order_id'] . ' not found, skipping' );
				continue;
			}

			$created_time = strtotime( $payment['created_at'] );
			$current_time = time();
			$age_minutes  = ( $current_time - $created_time ) / 60;

			self::log( 'info', 'Zano: Checking payment for order #' . $payment['order_id'] . ' (age: ' . round( $age_minutes, 1 ) . ' minutes)' );

			if ( empty( $payment['tx_hash'] ) && $age_minutes > 20 ) {
				self::log( 'warning', 'Zano: Marking payment for order #' . $payment['order_id'] . ' as expired (no TX hash after ' . round( $age_minutes, 1 ) . ' minutes)' );

				AnyCoin_Database_Manager::update_payment(
					(int) $payment['id'],
					[
						'status'     => 'failed',
						'updated_at' => current_time( 'mysql' ),
					]
				);

				$order->update_status( 'failed', 'Payment expired after 20 minutes without transaction' );
				$order->add_order_note( 'Payment automatically marked as failed - no transaction detected within 20 minutes' );
				$order->save();

				++$updated_count;
				continue;
			}

			if ( ! empty( $payment['tx_hash'] ) && $payment['status'] !== 'confirmed' ) {
				self::log( 'info', 'Zano: Verifying transaction ' . $payment['tx_hash'] . ' for order #' . $payment['order_id'] . ' (current status: ' . $payment['status'] . ', current confirmations: ' . ( $payment['confirmations'] ?? 0 ) . ')' );

				AnyCoin_Database_Manager::update_payment(
					(int) $payment['id'],
					[ 'updated_at' => current_time( 'mysql' ) ]
				);

				$explorer_url      = 'https://explorer.zano.org/api/get_tx_details/' . $payment['tx_hash'];
				$explorer_response = wp_remote_get(
					$explorer_url,
					[
						'timeout' => 10,
						'headers' => [
							'User-Agent' => 'AnyCoin-Payment-Gateway/1.0',
						],
					]
				);

				if ( is_wp_error( $explorer_response ) ) {
					self::log( 'error', 'Zano: Failed to get transaction details from explorer for ' . $payment['tx_hash'] . ': ' . $explorer_response->get_error_message() );
					continue;
				}

				$explorer_data = json_decode( wp_remote_retrieve_body( $explorer_response ), true );
				if ( ! $explorer_data || ! isset( $explorer_data['keeper_block'] ) ) {
					self::log( 'warning', 'Zano: Invalid response from explorer API for transaction ' . $payment['tx_hash'] );
					continue;
				}

				$keeper_block           = intval( $explorer_data['keeper_block'] ?? 0 );
				$last_block             = intval( $explorer_data['last_block'] ?? 0 );
				$verified_confirmations = max( 0, $last_block - $keeper_block );

				self::log(
					'debug',
					sprintf(
						'Zano: Explorer API result for TX %s: keeper_block=%d, last_block=%d, confirmations=%d',
						$payment['tx_hash'],
						$keeper_block,
						$last_block,
						$verified_confirmations
					)
				);

				$amount_to_verify    = ! empty( $payment['asset_amount'] ) ? $payment['asset_amount'] : $payment['amount'];
				$verification_result = self::verify_transaction_with_payment_id(
					$payment['tx_hash'],
					$payment['payment_id'],
					$payment['wallet_address'],
					$amount_to_verify,
					$api_url
				);

				self::log( 'info', 'Zano: Payment verification completed for transaction ' . $payment['tx_hash'] );

				if ( $verification_result['status'] === 'valid' ) {
					$received_amount        = floatval( $verification_result['received_amount'] ?? 0 );
					$asset_symbol           = $verification_result['asset_symbol'] ?? 'Unknown';
					$asset_id               = $verification_result['asset_id'] ?? '';
					$verification_method    = $verification_result['verification_method'] ?? 'unknown';
					$required_confirmations = intval( $gateway_settings['confirmations'] ?? 10 );

					$new_status = ( $verified_confirmations >= $required_confirmations ) ? 'confirmed' : 'processing';

					if ( $verification_method === 'fusd_payment_id_only' || $verification_method === 'fusd_payment_id_asset_id' ) {
						$status_note = sprintf(
							'FUSD payment verified by Payment ID. Amount: %f FUSD, Confirmations: %d/%d',
							$received_amount,
							$verified_confirmations,
							$required_confirmations
						);
					} else {
						$status_note = sprintf(
							'%s payment verified by Payment ID and amount. Amount: %f %s, Confirmations: %d/%d',
							$asset_symbol,
							$received_amount,
							$asset_symbol,
							$verified_confirmations,
							$required_confirmations
						);
					}

					$update_data = [
						'status'          => $new_status,
						'confirmations'   => $verified_confirmations,
						'received_amount' => $received_amount,
						'received_block'  => $keeper_block,
						'current_block'   => $last_block,
						'keeper_block'    => $keeper_block,
						'updated_at'      => current_time( 'mysql' ),
					];

					if ( $asset_id && empty( $payment['asset_id'] ) ) {
						$update_data['asset_id'] = $asset_id;
					}
					if ( $asset_symbol && empty( $payment['asset_symbol'] ) ) {
						$update_data['asset_symbol'] = $asset_symbol;
					}
					if ( $received_amount > 0 && empty( $payment['asset_amount'] ) ) {
						$update_data['asset_amount'] = $received_amount;
					}

					if ( $new_status === 'confirmed' ) {
						$update_data['completed_at'] = current_time( 'mysql' );
					}

					AnyCoin_Database_Manager::update_payment(
						(int) $payment['id'],
						$update_data
					);

					$order = wc_get_order( $payment['order_id'] );
					if ( $order && in_array( $order->get_status(), [ 'pending', 'on-hold' ], true ) ) {
						if ( $new_status === 'confirmed' ) {
							$order->payment_complete( $payment['tx_hash'] );
							$order->add_order_note(
								sprintf(
									'Payment confirmed via blockchain verification: %f %s received with %d confirmations (TX: %s)',
									$received_amount,
									$asset_symbol,
									$verified_confirmations,
									substr( $payment['tx_hash'], 0, 10 ) . '...'
								)
							);

							$order->update_meta_data( '_anycoin_payment_asset_symbol', $asset_symbol );
							$order->update_meta_data( '_anycoin_payment_asset_amount', $received_amount );
							$order->update_meta_data( '_anycoin_payment_asset_id', $asset_id );
							$order->update_meta_data( '_anycoin_payment_tx_hash', $payment['tx_hash'] );
							$order->update_meta_data( '_anycoin_verification_method', $verification_method );
						} else {
							$order->add_order_note( $status_note );
						}
						$order->save();
					}

					++$updated_count;
					self::log( 'info', 'Zano: Updated payment #' . $payment['id'] . ' for order #' . $payment['order_id'] . ' to status: ' . $new_status . ' (' . $verification_method . ')' );

				} elseif ( $verification_result['status'] === 'invalid' ) {
					self::log( 'warning', 'Zano: Transaction ' . $payment['tx_hash'] . ' payment ID verification failed for order #' . $payment['order_id'] . ': ' . $verification_result['message'] );

					$verification_attempts = intval( get_post_meta( $payment['order_id'], '_anycoin_verification_attempts', true ) );
					++$verification_attempts;
					update_post_meta( $payment['order_id'], '_anycoin_verification_attempts', $verification_attempts );

					if ( $verification_attempts >= 3 ) {
						AnyCoin_Database_Manager::update_payment(
							(int) $payment['id'],
							[
								'status'     => 'failed',
								'updated_at' => current_time( 'mysql' ),
							]
						);

						$order = wc_get_order( $payment['order_id'] );
						if ( $order ) {
							$order->update_status( 'failed', 'Payment verification failed after multiple attempts' );
							$order->add_order_note(
								sprintf(
									'Payment marked as failed after %d verification attempts. Last error: %s',
									$verification_attempts,
									$verification_result['message']
								)
							);
							$order->save();
						}

						++$updated_count;
					}
				} elseif ( $verification_result['status'] === 'error' ) {
					self::log( 'error', 'Zano: Transaction ' . $payment['tx_hash'] . ' payment ID verification error for order #' . $payment['order_id'] . ': ' . $verification_result['message'] );

				}

				usleep( 250000 ); // 0.25 second delay (reduced since we're using faster explorer API)
			}
		}

		self::log( 'info', 'Zano: Bulk status update completed. Updated ' . $updated_count . ' payments.' );

		return $updated_count;
	}

	/**
	 * Create default Zano icon from base64 data
	 */
	private static function get_default_icon_data() {
		return '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';
	}

	/**
	 * Verify transaction with payment ID for a specific payment (asset-aware verification).
	 *
	 * @param string $tx_hash         Transaction hash.
	 * @param string $payment_id      Expected payment ID.
	 * @param string $wallet_address  Wallet address.
	 * @param float  $expected_amount Expected amount.
	 * @param string $api_url         Node API URL.
	 * @return array Verification result.
	 */
	public static function verify_transaction_with_payment_id( $tx_hash, $payment_id, $wallet_address, $expected_amount, $api_url ) {
		$gateway_settings = get_option( 'woocommerce_anycoin_payment_settings', [] );
		$api_config       = [
			'api_url'            => $api_url,
			'wallet_address'     => $wallet_address,
			'view_key'           => $gateway_settings['view_key'] ?? '',
			'payment_id_api_url' => $gateway_settings['payment_id_api_url'] ?? AnyCoin_Constants::PAYMENT_VERIFICATION_API,
			'debug'              => isset( $gateway_settings['debug'] ) && $gateway_settings['debug'] === 'yes',
		];

		$api_instance = new AnyCoin_API( $api_config );

		try {
			global $wpdb;
			$table_name = $wpdb->prefix . AnyCoin_Constants::DB_TABLE_NAME;
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Internal read for verification context
			$payment_record = $wpdb->get_row(
				$wpdb->prepare(
					'SELECT * FROM %i WHERE tx_hash = %s AND payment_id = %s LIMIT 1',
					$wpdb->prefix . AnyCoin_Constants::DB_TABLE_NAME,
					$tx_hash,
					$payment_id
				)
			);

			$expected_payment_id = strtolower( (string) $payment_id );
			$record_asset_id     = ( $payment_record && ! empty( $payment_record->asset_id ) ) ? $payment_record->asset_id : null;

			// Intrinsic-PID claims (HF6) were already verified against
			// node-decrypted output data at claim time. The external decode API
			// cannot see intrinsic PIDs and the recent-blocks fallback only
			// covers ~5 blocks, so re-verifying the identifier here would
			// wrongly fail genuinely paid orders - skip straight to 'valid' and
			// let explorer confirmations drive completion.
			if ( $payment_record ) {
				$claimed_method = (string) get_post_meta( (int) $payment_record->order_id, '_anycoin_verification_method', true );
				if ( strpos( $claimed_method, 'intrinsic' ) !== false ) {
					$record_amount = ! empty( $payment_record->received_amount )
						? floatval( $payment_record->received_amount )
						: floatval( $expected_amount );

					return [
						'status'              => 'valid',
						'message'             => 'Payment previously verified via intrinsic payment ID (HF6)',
						'payment_id_verified' => true,
						'amount_verified'     => true,
						'asset_verified'      => true,
						'confirmations'       => intval( $payment_record->confirmations ?? 0 ),
						'received_amount'     => $record_amount,
						'expected_amount'     => floatval( $expected_amount ),
						'asset_id'            => $record_asset_id ? $record_asset_id : AnyCoin_Constants::ZANO_ASSET_ID,
						'asset_symbol'        => $payment_record->asset_symbol ? $payment_record->asset_symbol : 'ZANO',
						'verification_method' => $claimed_method,
					];
				}
			}

			$payment_verification = $api_instance->verify_payment_id( $tx_hash );

			$matched_payment = null;
			if ( ! is_wp_error( $payment_verification ) ) {
				$matched_payment = AnyCoin_API::extract_payment_for_pid( $payment_verification, $expected_payment_id, $record_asset_id );
			}

			$tx_seen_in_recent = false;

			if ( null === $matched_payment ) {
				// HF6 fallback: the external decode API cannot read intrinsic
				// per-output payment IDs, so re-check recent outputs from the
				// node, which decrypts intrinsic PIDs with the view key.
				$recent_transactions = $api_instance->get_recent_transactions();
				if ( ! is_wp_error( $recent_transactions ) ) {
					foreach ( $recent_transactions as $recent_tx ) {
						if ( ( $recent_tx['tx_hash'] ?? '' ) !== $tx_hash ) {
							continue;
						}
						$tx_seen_in_recent = true;
						if ( null !== $record_asset_id && ( $recent_tx['asset_id'] ?? '' ) !== $record_asset_id ) {
							continue;
						}
						$recent_pid = (string) ( $recent_tx['payment_id'] ?? '' );
						if ( '' !== $recent_pid && hash_equals( $expected_payment_id, $recent_pid ) ) {
							$matched_payment = [
								'paymentId' => $recent_pid,
								'amount'    => floatval( $recent_tx['amount'] ?? 0 ),
							];
							self::log( 'info', 'Zano: Verified transaction ' . $tx_hash . ' via intrinsic payment ID (HF6)' );
							break;
						}
					}
				}
			}

			if ( null === $matched_payment ) {
				if ( is_wp_error( $payment_verification ) ) {
					return [
						'status'  => 'error',
						'message' => $payment_verification->get_error_message(),
					];
				}

				if ( ! $tx_seen_in_recent ) {
					// Inconclusive, not a mismatch: the tx has left the
					// recent-blocks window, so the identifier cannot be
					// re-checked. Returning 'invalid' here would count a
					// failure strike against a possibly-paid order.
					return [
						'status'  => 'error',
						'message' => 'Transaction outside recent-blocks window; identifier re-check inconclusive',
					];
				}

				return [
					'status'  => 'invalid',
					'message' => sprintf( 'Payment ID mismatch: expected %s, no matching payment found in transaction', $payment_id ),
				];
			}

			$received_amount       = floatval( $matched_payment['amount'] ?? 0 );
			$expected_amount_float = floatval( $expected_amount );

			$is_fusd = false;
			if ( $payment_record && ! empty( $payment_record->asset_id ) ) {
				$is_fusd = ( $payment_record->asset_id === '86143388bd056a8f0bab669f78f14873fac8e2dd8d57898cdb725a2d5e2e4f8f' );
			} elseif ( $received_amount === 0.0 && $expected_amount_float > 0 ) {
				$is_fusd = true;
			}

			self::log(
				'debug',
				sprintf(
					'Zano: Payment verification - TX: %s, Expected: %f, Received: %f, Is FUSD: %s',
					$tx_hash,
					$expected_amount_float,
					$received_amount,
					$is_fusd ? 'Yes' : 'No'
				)
			);

			if ( $is_fusd ) {
				if ( $received_amount > 0 ) {
					// A real received amount is known (node-decoded) - hold FUSD
					// to the same tolerance as ZANO instead of skipping it.
					$fusd_tolerance = max( 0.0001, $expected_amount_float * 0.02 );
					if ( abs( $received_amount - $expected_amount_float ) > $fusd_tolerance ) {
						return [
							'status'  => 'invalid',
							'message' => sprintf(
								'FUSD amount mismatch: expected %s, received %s (tolerance: %s)',
								$expected_amount_float,
								$received_amount,
								$fusd_tolerance
							),
						];
					}
					$final_amount = $received_amount;
				} else {
					// No decoded amount available (legacy decode API returns none
					// for FUSD) - keep the amount recorded at claim time rather
					// than fabricating a full payment.
					self::log( 'info', 'Zano: FUSD transaction - no decoded amount available, using identifier verification only' );
					$final_amount = ! empty( $payment_record->received_amount )
						? floatval( $payment_record->received_amount )
						: $expected_amount_float;
				}

				if ( $payment_record ) {
					$update_data = [
						'updated_at' => current_time( 'mysql' ),
					];

					if ( empty( $payment_record->asset_id ) ) {
						$update_data['asset_id'] = '86143388bd056a8f0bab669f78f14873fac8e2dd8d57898cdb725a2d5e2e4f8f';
					}
					if ( empty( $payment_record->asset_symbol ) ) {
						$update_data['asset_symbol'] = 'FUSD';
					}
					if ( empty( $payment_record->asset_amount ) && $final_amount > 0 ) {
						$update_data['asset_amount'] = $final_amount;
					}
					if ( empty( $payment_record->received_amount ) && $final_amount > 0 ) {
						$update_data['received_amount'] = $final_amount;
					}

                    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Update asset info during verification
					$wpdb->update(
						$table_name,
						$update_data,
						[ 'id' => $payment_record->id ]
					);

					self::log( 'info', sprintf( 'Zano: Updated FUSD payment record #%d with asset info', $payment_record->id ) );
				}

				return [
					'status'              => 'valid',
					'message'             => 'FUSD payment verified by Payment ID (amount verification skipped)',
					'payment_id_verified' => true,
					'amount_verified'     => true,
					'asset_verified'      => true,
					'confirmations'       => intval( $payment_record->confirmations ?? 0 ),
					'received_amount'     => $final_amount,
					'expected_amount'     => $expected_amount_float,
					'asset_id'            => '86143388bd056a8f0bab669f78f14873fac8e2dd8d57898cdb725a2d5e2e4f8f',
					'asset_symbol'        => 'FUSD',
					'verification_method' => 'fusd_payment_id_only',
				];
			} else {
				$tolerance = max( 0.00000001, $expected_amount_float * 0.02 );

				if ( abs( $received_amount - $expected_amount_float ) > $tolerance ) {
					return [
						'status'  => 'invalid',
						'message' => sprintf(
							'ZANO amount mismatch: expected %s, received %s (tolerance: %s)',
							$expected_amount_float,
							$received_amount,
							$tolerance
						),
					];
				}

				if ( $payment_record ) {
					$update_data = [
						'received_amount' => $received_amount,
						'updated_at'      => current_time( 'mysql' ),
					];

					if ( empty( $payment_record->asset_id ) ) {
						$update_data['asset_id'] = 'd6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a';
					}
					if ( empty( $payment_record->asset_symbol ) ) {
						$update_data['asset_symbol'] = 'ZANO';
					}
					if ( empty( $payment_record->asset_amount ) ) {
						$update_data['asset_amount'] = $received_amount;
					}

                    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Update asset info during verification
					$wpdb->update(
						$table_name,
						$update_data,
						[ 'id' => $payment_record->id ]
					);
				}

				return [
					'status'              => 'valid',
					'message'             => 'ZANO payment verified by Payment ID and amount',
					'payment_id_verified' => true,
					'amount_verified'     => true,
					'asset_verified'      => true,
					'confirmations'       => intval( $payment_record->confirmations ?? 0 ),
					'received_amount'     => $received_amount,
					'expected_amount'     => $expected_amount_float,
					'asset_id'            => 'd6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a',
					'asset_symbol'        => 'ZANO',
					'verification_method' => 'zano_payment_id_amount',
				];
			}
		} catch ( Exception $e ) {
			self::log( 'error', 'Zano: Exception in verify_transaction_with_payment_id: ' . $e->getMessage() );
			return [
				'status'  => 'error',
				'message' => 'Verification failed: ' . $e->getMessage(),
			];
		}
	}
}
